--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
docs/markdown/examples.md HEAD (4fc8e03d) Text, 113.18 KB
Code Examples
A number of examples are included in the source distribution of Reticulum. You can use these examples to learn how to write your own programs.
Minimal
The Minimal example demonstrates the bare-minimum setup required to connect to a Reticulum network from your program. In about five lines of code, you will have the Reticulum Network Stack initialised, and ready to pass traffic in your program.
T282828
T8b949e##########################################################
T8b949e# This RNS example demonstrates a minimal setup, that #
T8b949e# will start up the Reticulum Network Stack, generate a #
T8b949e# new destination, and let the user send an announce. #
T8b949e##########################################################
Tff7b72import T7ee787argparse
Tff7b72import T7ee787sys
Tff7b72import T7ee787RNS
T8b949e# Let's define an app name. We'll use this for all
T8b949e# destinations we create. Since this basic example
T8b949e# is part of a range of example utilities, we'll put
T8b949e# them all within the app namespace "example_utilities"
Te6edf3APP_NAME Tff7b72= Ta5d6ff"Ta5d6ffexample_utilitiesTa5d6ff"
T8b949e# This initialisation is executed when the program is started
Tff7b72def Td2a8ffprogram_setupTb4b4b4(Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Randomly create a new identity for our example
Te6edf3identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTb4b4b4(Tb4b4b4)
T8b949e# Using the identity we just created, we create a destination.
T8b949e# Destinations are endpoints in Reticulum, that can be addressed
T8b949e# and communicated with. Destinations can also announce their
T8b949e# existence, which will let the network know they are reachable
T8b949e# and automatically create paths to them, from anywhere else
T8b949e# in the network.
Te6edf3destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffINTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffminimalsampleTa5d6ff"
Tb4b4b4)
T8b949e# We configure the destination to automatically prove all
T8b949e# packets addressed to it. By doing this, RNS will automatically
T8b949e# generate a proof for each incoming packet and transmit it
T8b949e# back to the sender of that packet. This will let anyone that
T8b949e# tries to communicate with the destination know whether their
T8b949e# communication was received correctly.
Te6edf3destinationTff7b72.Td2a8ffset_proof_strategyTb4b4b4(Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffPROVE_ALLTb4b4b4)
T8b949e# Everything's ready!
T8b949e# Let's hand over control to the announce loop
Te6edf3announceLoopTb4b4b4(Te6edf3destinationTb4b4b4)
Tff7b72def Td2a8ffannounceLoopTb4b4b4(Te6edf3destinationTb4b4b4)Tb4b4b4:
T8b949e# Let the user know that everything is ready
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffMinimal example Ta5d6ff"Tff7b72+
Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tff7b72+
Ta5d6ff"Ta5d6ff running, hit enter to manually send an announce (Ctrl-C to quit)Ta5d6ff"
Tb4b4b4)
T8b949e# We enter a loop that runs until the users exits.
T8b949e# If the user hits enter, we will announce our server
T8b949e# destination on the network, which will let clients
T8b949e# know how to create messages directed towards it.
Tff7b72while Tff7b72TrueTb4b4b4:
Te6edf3entered Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
Te6edf3destinationTff7b72.Td2a8ffannounceTb4b4b4(Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffSent announce from Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tb4b4b4)
T8b949e##########################################################
T8b949e#### Program Startup #####################################
T8b949e##########################################################
T8b949e# This part of the program gets run at startup,
T8b949e# and parses input from the user, and then starts
T8b949e# the desired program mode.
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3parser Tff7b72= Te6edf3argparseTff7b72.Td2a8ffArgumentParserTb4b4b4(
Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffMinimal example to start Reticulum and create a destinationTa5d6ff"
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff--configTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffpath to alternative Reticulum config directoryTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3args Tff7b72= Te6edf3parserTff7b72.Td2a8ffparse_argsTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3argsTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3configarg Tff7b72= Te6edf3argsTff7b72.Td2a8ffconfig
Tff7b72elseTb4b4b4:
Te6edf3configarg Tff7b72= Tff7b72None
Te6edf3program_setupTb4b4b4(Te6edf3configargTb4b4b4)
Tff7b72except Tf85149KeyboardInterruptTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
This example can also be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Minimal.py.
Announce
The Announce example builds upon the previous example by exploring how to announce a destination on the network, and how to let your program receive notifications about announces from relevant destinations.
T282828
T8b949e##########################################################
T8b949e# This RNS example demonstrates setting up announce #
T8b949e# callbacks, which will let an application receive a #
T8b949e# notification when an announce relevant for it arrives #
T8b949e##########################################################
Tff7b72import T7ee787argparse
Tff7b72import T7ee787random
Tff7b72import T7ee787sys
Tff7b72import T7ee787RNS
T8b949e# Let's define an app name. We'll use this for all
T8b949e# destinations we create. Since this basic example
T8b949e# is part of a range of example utilities, we'll put
T8b949e# them all within the app namespace "example_utilities"
Te6edf3APP_NAME Tff7b72= Ta5d6ff"Ta5d6ffexample_utilitiesTa5d6ff"
T8b949e# We initialise two lists of strings to use as app_data
Te6edf3fruits Tff7b72= Tb4b4b4[Ta5d6ff"Ta5d6ffPeachTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffQuinceTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffDateTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffTangerineTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffPomeloTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffCarambolaTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffGrapeTa5d6ff"Tb4b4b4]
Te6edf3noble_gases Tff7b72= Tb4b4b4[Ta5d6ff"Ta5d6ffHeliumTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffNeonTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffArgonTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffKryptonTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffXenonTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffRadonTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffOganessonTa5d6ff"Tb4b4b4]
T8b949e# This initialisation is executed when the program is started
Tff7b72def Td2a8ffprogram_setupTb4b4b4(Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Randomly create a new identity for our example
Te6edf3identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTb4b4b4(Tb4b4b4)
T8b949e# Using the identity we just created, we create two destinations
T8b949e# in the "example_utilities.announcesample" application space.
T8b949e#
T8b949e# Destinations are endpoints in Reticulum, that can be addressed
T8b949e# and communicated with. Destinations can also announce their
T8b949e# existence, which will let the network know they are reachable
T8b949e# and automatically create paths to them, from anywhere else
T8b949e# in the network.
Te6edf3destination_1 Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffINTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffannouncesampleTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fffruitsTa5d6ff"
Tb4b4b4)
Te6edf3destination_2 Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffINTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffannouncesampleTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffnoble_gasesTa5d6ff"
Tb4b4b4)
T8b949e# We configure the destinations to automatically prove all
T8b949e# packets addressed to it. By doing this, RNS will automatically
T8b949e# generate a proof for each incoming packet and transmit it
T8b949e# back to the sender of that packet. This will let anyone that
T8b949e# tries to communicate with the destination know whether their
T8b949e# communication was received correctly.
Te6edf3destination_1Tff7b72.Td2a8ffset_proof_strategyTb4b4b4(Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffPROVE_ALLTb4b4b4)
Te6edf3destination_2Tff7b72.Td2a8ffset_proof_strategyTb4b4b4(Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffPROVE_ALLTb4b4b4)
T8b949e# We create an announce handler and configure it to only ask for
T8b949e# announces from "example_utilities.announcesample.fruits".
T8b949e# Try changing the filter and see what happens.
Te6edf3announce_handler Tff7b72= Te6edf3ExampleAnnounceHandlerTb4b4b4(
Te6edf3aspect_filterTff7b72=Ta5d6ff"Ta5d6ffexample_utilities.announcesample.fruitsTa5d6ff"
Tb4b4b4)
T8b949e# We register the announce handler with Reticulum
Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffregister_announce_handlerTb4b4b4(Te6edf3announce_handlerTb4b4b4)
T8b949e# Everything's ready!
T8b949e# Let's hand over control to the announce loop
Te6edf3announceLoopTb4b4b4(Te6edf3destination_1Tb4b4b4, Te6edf3destination_2Tb4b4b4)
Tff7b72def Td2a8ffannounceLoopTb4b4b4(Te6edf3destination_1Tb4b4b4, Te6edf3destination_2Tb4b4b4)Tb4b4b4:
T8b949e# Let the user know that everything is ready
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffAnnounce example running, hit enter to manually send an announce (Ctrl-C to quit)Ta5d6ff"Tb4b4b4)
T8b949e# We enter a loop that runs until the users exits.
T8b949e# If the user hits enter, we will announce our server
T8b949e# destination on the network, which will let clients
T8b949e# know how to create messages directed towards it.
Tff7b72while Tff7b72TrueTb4b4b4:
Te6edf3entered Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
T8b949e# Randomly select a fruit
Te6edf3fruit Tff7b72= Te6edf3fruitsTb4b4b4[Te6edf3randomTff7b72.Td2a8ffrandintTb4b4b4(T79c0ff0Tb4b4b4,Tffa657lenTb4b4b4(Te6edf3fruitsTb4b4b4)Tff7b72-T79c0ff1Tb4b4b4)Tb4b4b4]
T8b949e# Send the announce including the app data
Te6edf3destination_1Tff7b72.Td2a8ffannounceTb4b4b4(Te6edf3app_dataTff7b72=Te6edf3fruitTff7b72.Td2a8ffencodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffSent announce from Ta5d6ff"Tff7b72+
Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destination_1Tff7b72.Td2a8ffhashTb4b4b4)Tff7b72+
Ta5d6ff"Ta5d6ff (Ta5d6ff"Tff7b72+Te6edf3destination_1Tff7b72.Td2a8ffnameTff7b72+Ta5d6ff"Ta5d6ff)Ta5d6ff"
Tb4b4b4)
T8b949e# Randomly select a noble gas
Te6edf3noble_gas Tff7b72= Te6edf3noble_gasesTb4b4b4[Te6edf3randomTff7b72.Td2a8ffrandintTb4b4b4(T79c0ff0Tb4b4b4,Tffa657lenTb4b4b4(Te6edf3noble_gasesTb4b4b4)Tff7b72-T79c0ff1Tb4b4b4)Tb4b4b4]
T8b949e# Send the announce including the app data
Te6edf3destination_2Tff7b72.Td2a8ffannounceTb4b4b4(Te6edf3app_dataTff7b72=Te6edf3noble_gasTff7b72.Td2a8ffencodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffSent announce from Ta5d6ff"Tff7b72+
Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destination_2Tff7b72.Td2a8ffhashTb4b4b4)Tff7b72+
Ta5d6ff"Ta5d6ff (Ta5d6ff"Tff7b72+Te6edf3destination_2Tff7b72.Td2a8ffnameTff7b72+Ta5d6ff"Ta5d6ff)Ta5d6ff"
Tb4b4b4)
T8b949e# We will need to define an announce handler class that
T8b949e# Reticulum can message when an announce arrives.
Tff7b72class T56d364ExampleAnnounceHandlerTb4b4b4:
T8b949e# The initialisation method takes the optional
T8b949e# aspect_filter argument. If aspect_filter is set to
T8b949e# None, all announces will be passed to the instance.
T8b949e# If only some announces are wanted, it can be set to
T8b949e# an aspect string.
Tff7b72def Tff7b72__init__Tb4b4b4(Tff7b72selfTb4b4b4, Te6edf3aspect_filterTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffaspect_filter Tff7b72= Te6edf3aspect_filter
T8b949e# This method will be called by Reticulums Transport
T8b949e# system when an announce arrives that matches the
T8b949e# configured aspect filter. Filters must be specific,
T8b949e# and cannot use wildcards.
Tff7b72def Td2a8ffreceived_announceTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3destination_hashTb4b4b4, Te6edf3announced_identityTb4b4b4, Te6edf3app_dataTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffReceived an announce from Ta5d6ff"Tff7b72+
Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destination_hashTb4b4b4)
Tb4b4b4)
Tff7b72if Te6edf3app_dataTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffThe announce contained the following app data: Ta5d6ff"Tff7b72+
Te6edf3app_dataTff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Tb4b4b4)
T8b949e##########################################################
T8b949e#### Program Startup #####################################
T8b949e##########################################################
T8b949e# This part of the program gets run at startup,
T8b949e# and parses input from the user, and then starts
T8b949e# the desired program mode.
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3parser Tff7b72= Te6edf3argparseTff7b72.Td2a8ffArgumentParserTb4b4b4(
Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffReticulum example that demonstrates announces and announce handlersTa5d6ff"
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff--configTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffpath to alternative Reticulum config directoryTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3args Tff7b72= Te6edf3parserTff7b72.Td2a8ffparse_argsTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3argsTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3configarg Tff7b72= Te6edf3argsTff7b72.Td2a8ffconfig
Tff7b72elseTb4b4b4:
Te6edf3configarg Tff7b72= Tff7b72None
Te6edf3program_setupTb4b4b4(Te6edf3configargTb4b4b4)
Tff7b72except Tf85149KeyboardInterruptTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
This example can also be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Announce.py.
Broadcast
The Broadcast example explores how to transmit plaintext broadcast messages over the network.
T282828
T8b949e##########################################################
T8b949e# This RNS example demonstrates broadcasting unencrypted #
T8b949e# information to any listening destinations. #
T8b949e##########################################################
Tff7b72import T7ee787sys
Tff7b72import T7ee787argparse
Tff7b72import T7ee787RNS
T8b949e# Let's define an app name. We'll use this for all
T8b949e# destinations we create. Since this basic example
T8b949e# is part of a range of example utilities, we'll put
T8b949e# them all within the app namespace "example_utilities"
Te6edf3APP_NAME Tff7b72= Ta5d6ff"Ta5d6ffexample_utilitiesTa5d6ff"
T8b949e# This initialisation is executed when the program is started
Tff7b72def Td2a8ffprogram_setupTb4b4b4(Te6edf3configpathTb4b4b4, Te6edf3channelTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4:
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# If the user did not select a "channel" we use
T8b949e# a default one called "public_information".
T8b949e# This "channel" is added to the destination name-
T8b949e# space, so the user can select different broadcast
T8b949e# channels.
Tff7b72if Te6edf3channel Tff7b72== Tff7b72NoneTb4b4b4:
Te6edf3channel Tff7b72= Ta5d6ff"Ta5d6ffpublic_informationTa5d6ff"
T8b949e# We create a PLAIN destination. This is an uncencrypted endpoint
T8b949e# that anyone can listen to and send information to.
Te6edf3broadcast_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Tff7b72NoneTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffINTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffPLAINTb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffbroadcastTa5d6ff"Tb4b4b4,
Te6edf3channel
Tb4b4b4)
T8b949e# We specify a callback that will get called every time
T8b949e# the destination receives data.
Te6edf3broadcast_destinationTff7b72.Td2a8ffset_packet_callbackTb4b4b4(Te6edf3packet_callbackTb4b4b4)
T8b949e# Everything's ready!
T8b949e# Let's hand over control to the main loop
Te6edf3broadcastLoopTb4b4b4(Te6edf3broadcast_destinationTb4b4b4)
Tff7b72def Td2a8ffpacket_callbackTb4b4b4(Te6edf3dataTb4b4b4, Te6edf3packetTb4b4b4)Tb4b4b4:
T8b949e# Simply print out the received data
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffReceived data: Ta5d6ff"Tff7b72+Te6edf3dataTff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)Tff7b72+Ta5d6ff"Tffea00\rTffea00\nTa5d6ff> Ta5d6ff"Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffstdoutTff7b72.Td2a8ffflushTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ffbroadcastLoopTb4b4b4(Te6edf3destinationTb4b4b4)Tb4b4b4:
T8b949e# Let the user know that everything is ready
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffBroadcast example Ta5d6ff"Tff7b72+
Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tff7b72+
Ta5d6ff"Ta5d6ff running, enter text and hit enter to broadcast (Ctrl-C to quit)Ta5d6ff"
Tb4b4b4)
T8b949e# We enter a loop that runs until the users exits.
T8b949e# If the user hits enter, we will send the information
T8b949e# that the user entered into the prompt.
Tff7b72while Tff7b72TrueTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff> Ta5d6ff"Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3entered Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3entered Tff7b72!= Ta5d6ff"Ta5d6ff"Tb4b4b4:
Te6edf3data Tff7b72= Te6edf3enteredTff7b72.Td2a8ffencodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Te6edf3packet Tff7b72= Te6edf3RNSTff7b72.Td2a8ffPacketTb4b4b4(Te6edf3destinationTb4b4b4, Te6edf3dataTb4b4b4)
Te6edf3packetTff7b72.Td2a8ffsendTb4b4b4(Tb4b4b4)
T8b949e##########################################################
T8b949e#### Program Startup #####################################
T8b949e##########################################################
T8b949e# This part of the program gets run at startup,
T8b949e# and parses input from the user, and then starts
T8b949e# the program.
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3parser Tff7b72= Te6edf3argparseTff7b72.Td2a8ffArgumentParserTb4b4b4(
Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffReticulum example demonstrating sending and receiving broadcastsTa5d6ff"
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff--configTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffpath to alternative Reticulum config directoryTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff--channelTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffbroadcast channel nameTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3args Tff7b72= Te6edf3parserTff7b72.Td2a8ffparse_argsTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3argsTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3configarg Tff7b72= Te6edf3argsTff7b72.Td2a8ffconfig
Tff7b72elseTb4b4b4:
Te6edf3configarg Tff7b72= Tff7b72None
Tff7b72if Te6edf3argsTff7b72.Td2a8ffchannelTb4b4b4:
Te6edf3channelarg Tff7b72= Te6edf3argsTff7b72.Td2a8ffchannel
Tff7b72elseTb4b4b4:
Te6edf3channelarg Tff7b72= Tff7b72None
Te6edf3program_setupTb4b4b4(Te6edf3configargTb4b4b4, Te6edf3channelargTb4b4b4)
Tff7b72except Tf85149KeyboardInterruptTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
This example can also be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Broadcast.py.
Echo
The Echo example demonstrates communication between two destinations using the Packet interface.
T282828
T8b949e##########################################################
T8b949e# This RNS example demonstrates a simple client/server #
T8b949e# echo utility. A client can send an echo request to the #
T8b949e# server, and the server will respond by proving receipt #
T8b949e# of the packet. #
T8b949e##########################################################
Tff7b72import T7ee787argparse
Tff7b72import T7ee787sys
Tff7b72import T7ee787RNS
T8b949e# Let's define an app name. We'll use this for all
T8b949e# destinations we create. Since this echo example
T8b949e# is part of a range of example utilities, we'll put
T8b949e# them all within the app namespace "example_utilities"
Te6edf3APP_NAME Tff7b72= Ta5d6ff"Ta5d6ffexample_utilitiesTa5d6ff"
T8b949e##########################################################
T8b949e#### Server Part #########################################
T8b949e##########################################################
T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a server
Tff7b72def Td2a8ffserverTb4b4b4(Te6edf3configpathTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3reticulum
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Randomly create a new identity for our echo server
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTb4b4b4(Tb4b4b4)
T8b949e# We create a destination that clients can query. We want
T8b949e# to be able to verify echo replies to our clients, so we
T8b949e# create a "single" destination that can receive encrypted
T8b949e# messages. This way the client can send a request and be
T8b949e# certain that no-one else than this destination was able
T8b949e# to read it.
Te6edf3echo_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffINTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffechoTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffrequestTa5d6ff"
Tb4b4b4)
T8b949e# We configure the destination to automatically prove all
T8b949e# packets addressed to it. By doing this, RNS will automatically
T8b949e# generate a proof for each incoming packet and transmit it
T8b949e# back to the sender of that packet.
Te6edf3echo_destinationTff7b72.Td2a8ffset_proof_strategyTb4b4b4(Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffPROVE_ALLTb4b4b4)
T8b949e# Tell the destination which function in our program to
T8b949e# run when a packet is received. We do this so we can
T8b949e# print a log message when the server receives a request
Te6edf3echo_destinationTff7b72.Td2a8ffset_packet_callbackTb4b4b4(Te6edf3server_callbackTb4b4b4)
T8b949e# Everything's ready!
T8b949e# Let's Wait for client requests or user input
Te6edf3announceLoopTb4b4b4(Te6edf3echo_destinationTb4b4b4)
Tff7b72def Td2a8ffannounceLoopTb4b4b4(Te6edf3destinationTb4b4b4)Tb4b4b4:
T8b949e# Let the user know that everything is ready
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffEcho server Ta5d6ff"Tff7b72+
Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tff7b72+
Ta5d6ff"Ta5d6ff running, hit enter to manually send an announce (Ctrl-C to quit)Ta5d6ff"
Tb4b4b4)
T8b949e# We enter a loop that runs until the users exits.
T8b949e# If the user hits enter, we will announce our server
T8b949e# destination on the network, which will let clients
T8b949e# know how to create messages directed towards it.
Tff7b72while Tff7b72TrueTb4b4b4:
Te6edf3entered Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
Te6edf3destinationTff7b72.Td2a8ffannounceTb4b4b4(Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffSent announce from Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tb4b4b4)
Tff7b72def Td2a8ffserver_callbackTb4b4b4(Te6edf3messageTb4b4b4, Te6edf3packetTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3reticulum
T8b949e# Tell the user that we received an echo request, and
T8b949e# that we are going to send a reply to the requester.
T8b949e# Sending the proof is handled automatically, since we
T8b949e# set up the destination to prove all incoming packets.
Te6edf3reception_stats Tff7b72= Ta5d6ff"Ta5d6ff"
Tff7b72if Te6edf3reticulumTff7b72.Td2a8ffis_connected_to_shared_instanceTb4b4b4:
Te6edf3reception_rssi Tff7b72= Te6edf3reticulumTff7b72.Td2a8ffget_packet_rssiTb4b4b4(Te6edf3packetTff7b72.Td2a8ffpacket_hashTb4b4b4)
Te6edf3reception_snr Tff7b72= Te6edf3reticulumTff7b72.Td2a8ffget_packet_snrTb4b4b4(Te6edf3packetTff7b72.Td2a8ffpacket_hashTb4b4b4)
Tff7b72if Te6edf3reception_rssi Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3reception_stats Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ff [RSSI Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3reception_rssiTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff dBm]Ta5d6ff"
Tff7b72if Te6edf3reception_snr Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3reception_stats Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ff [SNR Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3reception_snrTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff dBm]Ta5d6ff"
Tff7b72elseTb4b4b4:
Tff7b72if Te6edf3packetTff7b72.Td2a8ffrssi Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3reception_stats Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ff [RSSI Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3packetTff7b72.Td2a8ffrssiTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff dBm]Ta5d6ff"
Tff7b72if Te6edf3packetTff7b72.Td2a8ffsnr Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3reception_stats Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ff [SNR Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3packetTff7b72.Td2a8ffsnrTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff dB]Ta5d6ff"
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffReceived packet from echo client, proof sentTa5d6ff"Tff7b72+Te6edf3reception_statsTb4b4b4)
T8b949e##########################################################
T8b949e#### Client Part #########################################
T8b949e##########################################################
T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a client
Tff7b72def Td2a8ffclientTb4b4b4(Te6edf3destination_hexhashTb4b4b4, Te6edf3configpathTb4b4b4, Te6edf3timeoutTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3reticulum
T8b949e# We need a binary representation of the destination
T8b949e# hash that was entered on the command line
Tff7b72tryTb4b4b4:
Te6edf3dest_len Tff7b72= Tb4b4b4(Te6edf3RNSTff7b72.Td2a8ffReticulumTff7b72.Td2a8ffTRUNCATED_HASHLENGTHTff7b72/Tff7b72/T79c0ff8Tb4b4b4)Tff7b72*T79c0ff2
Tff7b72if Tffa657lenTb4b4b4(Te6edf3destination_hexhashTb4b4b4) Tff7b72!= Te6edf3dest_lenTb4b4b4:
Tff7b72raise Tf85149ValueErrorTb4b4b4(
Ta5d6ff"Ta5d6ffDestination length is invalid, must be Tffd700{hex}Ta5d6ff hexadecimal characters (Tffd700{byte}Ta5d6ff bytes).Ta5d6ff"Tff7b72.Td2a8ffformatTb4b4b4(Tffa657hexTff7b72=Te6edf3dest_lenTb4b4b4, Te6edf3byteTff7b72=Te6edf3dest_lenTff7b72/Tff7b72/T79c0ff2Tb4b4b4)
Tb4b4b4)
Te6edf3destination_hash Tff7b72= Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Te6edf3destination_hexhashTb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffInvalid destination entered. Check your input!Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tff7b72+Ta5d6ff"Tffea00\nTa5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# We override the loglevel to provide feedback when
T8b949e# an announce is received
Tff7b72if Te6edf3RNSTff7b72.Td2a8ffloglevel Tff7b72< Te6edf3RNSTff7b72.Td2a8ffLOG_INFOTb4b4b4:
Te6edf3RNSTff7b72.Td2a8ffloglevel Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLOG_INFO
T8b949e# Tell the user that the client is ready!
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffEcho client ready, hit enter to send echo request to Ta5d6ff"Tff7b72+
Te6edf3destination_hexhashTff7b72+
Ta5d6ff"Ta5d6ff (Ctrl-C to quit)Ta5d6ff"
Tb4b4b4)
T8b949e# We enter a loop that runs until the user exits.
T8b949e# If the user hits enter, we will try to send an
T8b949e# echo request to the destination specified on the
T8b949e# command line.
Tff7b72while Tff7b72TrueTb4b4b4:
Tffa657inputTb4b4b4(Tb4b4b4)
T8b949e# Let's first check if RNS knows a path to the destination.
T8b949e# If it does, we'll load the server identity and create a packet
Tff7b72if Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
T8b949e# To address the server, we need to know it's public
T8b949e# key, so we check if Reticulum knows this destination.
T8b949e# This is done by calling the "recall" method of the
T8b949e# Identity module. If the destination is known, it will
T8b949e# return an Identity instance that can be used in
T8b949e# outgoing destinations.
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTff7b72.Td2a8ffrecallTb4b4b4(Te6edf3destination_hashTb4b4b4)
T8b949e# We got the correct identity instance from the
T8b949e# recall method, so let's create an outgoing
T8b949e# destination. We use the naming convention:
T8b949e# example_utilities.echo.request
T8b949e# This matches the naming we specified in the
T8b949e# server part of the code.
Te6edf3request_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffOUTTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffechoTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffrequestTa5d6ff"
Tb4b4b4)
T8b949e# The destination is ready, so let's create a packet.
T8b949e# We set the destination to the request_destination
T8b949e# that was just created, and the only data we add
T8b949e# is a random hash.
Te6edf3echo_request Tff7b72= Te6edf3RNSTff7b72.Td2a8ffPacketTb4b4b4(Te6edf3request_destinationTb4b4b4, Te6edf3RNSTff7b72.Td2a8ffIdentityTff7b72.Td2a8ffget_random_hashTb4b4b4(Tb4b4b4)Tb4b4b4)
T8b949e# Send the packet! If the packet is successfully
T8b949e# sent, it will return a PacketReceipt instance.
Te6edf3packet_receipt Tff7b72= Te6edf3echo_requestTff7b72.Td2a8ffsendTb4b4b4(Tb4b4b4)
T8b949e# If the user specified a timeout, we set this
T8b949e# timeout on the packet receipt, and configure
T8b949e# a callback function, that will get called if
T8b949e# the packet times out.
Tff7b72if Te6edf3timeout Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3packet_receiptTff7b72.Td2a8ffset_timeoutTb4b4b4(Te6edf3timeoutTb4b4b4)
Te6edf3packet_receiptTff7b72.Td2a8ffset_timeout_callbackTb4b4b4(Te6edf3packet_timed_outTb4b4b4)
T8b949e# We can then set a delivery callback on the receipt.
T8b949e# This will get automatically called when a proof for
T8b949e# this specific packet is received from the destination.
Te6edf3packet_receiptTff7b72.Td2a8ffset_delivery_callbackTb4b4b4(Te6edf3packet_deliveredTb4b4b4)
T8b949e# Tell the user that the echo request was sent
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffSent echo request to Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3request_destinationTff7b72.Td2a8ffhashTb4b4b4)Tb4b4b4)
Tff7b72elseTb4b4b4:
T8b949e# If we do not know this destination, tell the
T8b949e# user to wait for an announce to arrive.
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffDestination is not yet known. Requesting path...Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffHit enter to manually retry once an announce is received.Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffrequest_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)
T8b949e# This function is called when our reply destination
T8b949e# receives a proof packet.
Tff7b72def Td2a8ffpacket_deliveredTb4b4b4(Te6edf3receiptTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3reticulum
Tff7b72if Te6edf3receiptTff7b72.Td2a8ffstatus Tff7b72== Te6edf3RNSTff7b72.Td2a8ffPacketReceiptTff7b72.Td2a8ffDELIVEREDTb4b4b4:
Te6edf3rtt Tff7b72= Te6edf3receiptTff7b72.Td2a8ffget_rttTb4b4b4(Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3rtt Tff7b72>Tff7b72= T79c0ff1Tb4b4b4)Tb4b4b4:
Te6edf3rtt Tff7b72= Tffa657roundTb4b4b4(Te6edf3rttTb4b4b4, T79c0ff3Tb4b4b4)
Te6edf3rttstring Tff7b72= Tffa657strTb4b4b4(Te6edf3rttTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff secondsTa5d6ff"
Tff7b72elseTb4b4b4:
Te6edf3rtt Tff7b72= Tffa657roundTb4b4b4(Te6edf3rttTff7b72*T79c0ff1000Tb4b4b4, T79c0ff3Tb4b4b4)
Te6edf3rttstring Tff7b72= Tffa657strTb4b4b4(Te6edf3rttTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff millisecondsTa5d6ff"
Te6edf3reception_stats Tff7b72= Ta5d6ff"Ta5d6ff"
Tff7b72if Te6edf3reticulumTff7b72.Td2a8ffis_connected_to_shared_instanceTb4b4b4:
Te6edf3reception_rssi Tff7b72= Te6edf3reticulumTff7b72.Td2a8ffget_packet_rssiTb4b4b4(Te6edf3receiptTff7b72.Td2a8ffproof_packetTff7b72.Td2a8ffpacket_hashTb4b4b4)
Te6edf3reception_snr Tff7b72= Te6edf3reticulumTff7b72.Td2a8ffget_packet_snrTb4b4b4(Te6edf3receiptTff7b72.Td2a8ffproof_packetTff7b72.Td2a8ffpacket_hashTb4b4b4)
Tff7b72if Te6edf3reception_rssi Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3reception_stats Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ff [RSSI Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3reception_rssiTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff dBm]Ta5d6ff"
Tff7b72if Te6edf3reception_snr Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3reception_stats Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ff [SNR Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3reception_snrTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff dB]Ta5d6ff"
Tff7b72elseTb4b4b4:
Tff7b72if Te6edf3receiptTff7b72.Td2a8ffproof_packet Tff7b72!= Tff7b72NoneTb4b4b4:
Tff7b72if Te6edf3receiptTff7b72.Td2a8ffproof_packetTff7b72.Td2a8ffrssi Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3reception_stats Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ff [RSSI Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3receiptTff7b72.Td2a8ffproof_packetTff7b72.Td2a8ffrssiTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff dBm]Ta5d6ff"
Tff7b72if Te6edf3receiptTff7b72.Td2a8ffproof_packetTff7b72.Td2a8ffsnr Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3reception_stats Tff7b72+Tff7b72= Ta5d6ff"Ta5d6ff [SNR Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3receiptTff7b72.Td2a8ffproof_packetTff7b72.Td2a8ffsnrTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff dB]Ta5d6ff"
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffValid reply received from Ta5d6ff"Tff7b72+
Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3receiptTff7b72.Td2a8ffdestinationTff7b72.Td2a8ffhashTb4b4b4)Tff7b72+
Ta5d6ff"Ta5d6ff, round-trip time is Ta5d6ff"Tff7b72+Te6edf3rttstringTff7b72+
Te6edf3reception_stats
Tb4b4b4)
T8b949e# This function is called if a packet times out.
Tff7b72def Td2a8ffpacket_timed_outTb4b4b4(Te6edf3receiptTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3receiptTff7b72.Td2a8ffstatus Tff7b72== Te6edf3RNSTff7b72.Td2a8ffPacketReceiptTff7b72.Td2a8ffFAILEDTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffPacket Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3receiptTff7b72.Td2a8ffhashTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff timed outTa5d6ff"Tb4b4b4)
T8b949e##########################################################
T8b949e#### Program Startup #####################################
T8b949e##########################################################
T8b949e# This part of the program gets run at startup,
T8b949e# and parses input from the user, and then starts
T8b949e# the desired program mode.
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3parser Tff7b72= Te6edf3argparseTff7b72.Td2a8ffArgumentParserTb4b4b4(Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffSimple echo server and client utilityTa5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff-sTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--serverTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstore_trueTa5d6ff"Tb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffwait for incoming packets from clientsTa5d6ff"
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff-tTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--timeoutTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3metavarTff7b72=Ta5d6ff"Ta5d6ffsTa5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffset a reply timeout in secondsTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657float
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(Ta5d6ff"Ta5d6ff--configTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffpath to alternative Reticulum config directoryTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ffdestinationTa5d6ff"Tb4b4b4,
Te6edf3nargsTff7b72=Ta5d6ff"Ta5d6ff?Ta5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffhexadecimal hash of the server destinationTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3args Tff7b72= Te6edf3parserTff7b72.Td2a8ffparse_argsTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3argsTff7b72.Td2a8ffserverTb4b4b4:
Te6edf3configargTff7b72=Tff7b72None
Tff7b72if Te6edf3argsTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3configarg Tff7b72= Te6edf3argsTff7b72.Td2a8ffconfig
Te6edf3serverTb4b4b4(Te6edf3configargTb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72if Te6edf3argsTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3configarg Tff7b72= Te6edf3argsTff7b72.Td2a8ffconfig
Tff7b72elseTb4b4b4:
Te6edf3configarg Tff7b72= Tff7b72None
Tff7b72if Te6edf3argsTff7b72.Td2a8fftimeoutTb4b4b4:
Te6edf3timeoutarg Tff7b72= Tffa657floatTb4b4b4(Te6edf3argsTff7b72.Td2a8fftimeoutTb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3timeoutarg Tff7b72= Tff7b72None
Tff7b72if Tb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestination Tff7b72== Tff7b72NoneTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffprint_helpTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3clientTb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestinationTb4b4b4, Te6edf3configargTb4b4b4, Te6edf3timeoutTff7b72=Te6edf3timeoutargTb4b4b4)
Tff7b72except Tf85149KeyboardInterruptTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
This example can also be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Echo.py.
Link
The Link example explores establishing an encrypted link to a remote destination, and passing traffic back and forth over the link.
T282828
T8b949e##########################################################
T8b949e# This RNS example demonstrates how to set up a link to #
T8b949e# a destination, and pass data back and forth over it. #
T8b949e##########################################################
Tff7b72import T7ee787os
Tff7b72import T7ee787sys
Tff7b72import T7ee787time
Tff7b72import T7ee787argparse
Tff7b72import T7ee787RNS
T8b949e# Let's define an app name. We'll use this for all
T8b949e# destinations we create. Since this echo example
T8b949e# is part of a range of example utilities, we'll put
T8b949e# them all within the app namespace "example_utilities"
Te6edf3APP_NAME Tff7b72= Ta5d6ff"Ta5d6ffexample_utilitiesTa5d6ff"
T8b949e##########################################################
T8b949e#### Server Part #########################################
T8b949e##########################################################
T8b949e# A reference to the latest client link that connected
Te6edf3latest_client_link Tff7b72= Tff7b72None
T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a server
Tff7b72def Td2a8ffserverTb4b4b4(Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Randomly create a new identity for our link example
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTb4b4b4(Tb4b4b4)
T8b949e# We create a destination that clients can connect to. We
T8b949e# want clients to create links to this destination, so we
T8b949e# need to create a "single" destination type.
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffINTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6fflinkexampleTa5d6ff"
Tb4b4b4)
T8b949e# We configure a function that will get called every time
T8b949e# a new client creates a link to this destination.
Te6edf3server_destinationTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3client_connectedTb4b4b4)
T8b949e# Everything's ready!
T8b949e# Let's Wait for client requests or user input
Te6edf3server_loopTb4b4b4(Te6edf3server_destinationTb4b4b4)
Tff7b72def Td2a8ffserver_loopTb4b4b4(Te6edf3destinationTb4b4b4)Tb4b4b4:
T8b949e# Let the user know that everything is ready
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffLink example Ta5d6ff"Tff7b72+
Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tff7b72+
Ta5d6ff"Ta5d6ff running, waiting for a connection.Ta5d6ff"
Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffHit enter to manually send an announce (Ctrl-C to quit)Ta5d6ff"Tb4b4b4)
T8b949e# We enter a loop that runs until the users exits.
T8b949e# If the user hits enter, we will announce our server
T8b949e# destination on the network, which will let clients
T8b949e# know how to create messages directed towards it.
Tff7b72while Tff7b72TrueTb4b4b4:
Te6edf3entered Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
Te6edf3destinationTff7b72.Td2a8ffannounceTb4b4b4(Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffSent announce from Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tb4b4b4)
T8b949e# When a client establishes a link to our server
T8b949e# destination, this function will be called with
T8b949e# a reference to the link.
Tff7b72def Td2a8ffclient_connectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3latest_client_link
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient connectedTa5d6ff"Tb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3client_disconnectedTb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_packet_callbackTb4b4b4(Te6edf3server_packet_receivedTb4b4b4)
Te6edf3latest_client_link Tff7b72= Te6edf3link
Tff7b72def Td2a8ffclient_disconnectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient disconnectedTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffserver_packet_receivedTb4b4b4(Te6edf3messageTb4b4b4, Te6edf3packetTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3latest_client_link
T8b949e# When data is received over any active link,
T8b949e# it will all be directed to the last client
T8b949e# that connected.
Te6edf3text Tff7b72= Te6edf3messageTff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffReceived data on the link: Ta5d6ff"Tff7b72+Te6edf3textTb4b4b4)
Te6edf3reply_text Tff7b72= Ta5d6ff"Ta5d6ffI received Tffea00\"Ta5d6ff"Tff7b72+Te6edf3textTff7b72+Ta5d6ff"Tffea00\"Ta5d6ff over the linkTa5d6ff"
Te6edf3reply_data Tff7b72= Te6edf3reply_textTff7b72.Td2a8ffencodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8ffPacketTb4b4b4(Te6edf3latest_client_linkTb4b4b4, Te6edf3reply_dataTb4b4b4)Tff7b72.Td2a8ffsendTb4b4b4(Tb4b4b4)
T8b949e##########################################################
T8b949e#### Client Part #########################################
T8b949e##########################################################
T8b949e# A reference to the server link
Te6edf3server_link Tff7b72= Tff7b72None
T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a client
Tff7b72def Td2a8ffclientTb4b4b4(Te6edf3destination_hexhashTb4b4b4, Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We need a binary representation of the destination
T8b949e# hash that was entered on the command line
Tff7b72tryTb4b4b4:
Te6edf3dest_len Tff7b72= Tb4b4b4(Te6edf3RNSTff7b72.Td2a8ffReticulumTff7b72.Td2a8ffTRUNCATED_HASHLENGTHTff7b72/Tff7b72/T79c0ff8Tb4b4b4)Tff7b72*T79c0ff2
Tff7b72if Tffa657lenTb4b4b4(Te6edf3destination_hexhashTb4b4b4) Tff7b72!= Te6edf3dest_lenTb4b4b4:
Tff7b72raise Tf85149ValueErrorTb4b4b4(
Ta5d6ff"Ta5d6ffDestination length is invalid, must be Tffd700{hex}Ta5d6ff hexadecimal characters (Tffd700{byte}Ta5d6ff bytes).Ta5d6ff"Tff7b72.Td2a8ffformatTb4b4b4(Tffa657hexTff7b72=Te6edf3dest_lenTb4b4b4, Te6edf3byteTff7b72=Te6edf3dest_lenTff7b72/Tff7b72/T79c0ff2Tb4b4b4)
Tb4b4b4)
Te6edf3destination_hash Tff7b72= Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Te6edf3destination_hexhashTb4b4b4)
Tff7b72exceptTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffInvalid destination entered. Check your input!Tffea00\nTa5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Check if we know a path to the destination
Tff7b72if Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffDestination is not yet known. Requesting path and waiting for announce to arrive...Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffrequest_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)
Tff7b72while Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)
T8b949e# Recall the server identity
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTff7b72.Td2a8ffrecallTb4b4b4(Te6edf3destination_hashTb4b4b4)
T8b949e# Inform the user that we'll begin connecting
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffEstablishing link with server...Ta5d6ff"Tb4b4b4)
T8b949e# When the server identity is known, we set
T8b949e# up a destination
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffOUTTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6fflinkexampleTa5d6ff"
Tb4b4b4)
T8b949e# And create a link
Te6edf3link Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTb4b4b4(Te6edf3server_destinationTb4b4b4)
T8b949e# We set a callback that will get executed
T8b949e# every time a packet is received over the
T8b949e# link
Te6edf3linkTff7b72.Td2a8ffset_packet_callbackTb4b4b4(Te6edf3client_packet_receivedTb4b4b4)
T8b949e# We'll also set up functions to inform the
T8b949e# user when the link is established or closed
Te6edf3linkTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3link_establishedTb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3link_closedTb4b4b4)
T8b949e# Everything is set up, so let's enter a loop
T8b949e# for the user to interact with the example
Te6edf3client_loopTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ffclient_loopTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3server_link
T8b949e# Wait for the link to become active
Tff7b72while Tff7b72not Te6edf3server_linkTb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)
Te6edf3should_quit Tff7b72= Tff7b72False
Tff7b72while Tff7b72not Te6edf3should_quitTb4b4b4:
Tff7b72tryTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff> Ta5d6ff"Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4)
Te6edf3text Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
T8b949e# Check if we should quit the example
Tff7b72if Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffquitTa5d6ff" Tff7b72or Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffqTa5d6ff" Tff7b72or Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffexitTa5d6ff"Tb4b4b4:
Te6edf3should_quit Tff7b72= Tff7b72True
Te6edf3server_linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
T8b949e# If not, send the entered text over the link
Tff7b72if Te6edf3text Tff7b72!= Ta5d6ff"Ta5d6ff"Tb4b4b4:
Te6edf3data Tff7b72= Te6edf3textTff7b72.Td2a8ffencodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Tff7b72if Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4) Tff7b72<Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffMDUTb4b4b4:
Te6edf3RNSTff7b72.Td2a8ffPacketTb4b4b4(Te6edf3server_linkTb4b4b4, Te6edf3dataTb4b4b4)Tff7b72.Td2a8ffsendTb4b4b4(Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffCannot send this packet, the data size of Ta5d6ff"Tff7b72+
Tffa657strTb4b4b4(Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4)Tb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff bytes exceeds the link packet MDU of Ta5d6ff"Tff7b72+
Tffa657strTb4b4b4(Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffMDUTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff bytesTa5d6ff"Tb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffLOG_ERROR
Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffError while sending data over the link: Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4)
Te6edf3should_quit Tff7b72= Tff7b72True
Te6edf3server_linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
T8b949e# This function is called when a link
T8b949e# has been established with the server
Tff7b72def Td2a8fflink_establishedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
T8b949e# We store a reference to the link
T8b949e# instance for later use
Tff7b72global Te6edf3server_link
Te6edf3server_link Tff7b72= Te6edf3link
T8b949e# Inform the user that the server is
T8b949e# connected
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink established with server, enter some text to send, or Tffea00\"Ta5d6ffquitTffea00\"Ta5d6ff to quitTa5d6ff"Tb4b4b4)
T8b949e# When a link is closed, we'll inform the
T8b949e# user, and exit the program
Tff7b72def Td2a8fflink_closedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffTIMEOUTTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link timed out, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elif Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffDESTINATION_CLOSEDTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link was closed by the server, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink closed, exiting nowTa5d6ff"Tb4b4b4)
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff1.5Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
T8b949e# When a packet is received over the link, we
T8b949e# simply print out the data.
Tff7b72def Td2a8ffclient_packet_receivedTb4b4b4(Te6edf3messageTb4b4b4, Te6edf3packetTb4b4b4)Tb4b4b4:
Te6edf3text Tff7b72= Te6edf3messageTff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffReceived data on the link: Ta5d6ff"Tff7b72+Te6edf3textTb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff> Ta5d6ff"Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffstdoutTff7b72.Td2a8ffflushTb4b4b4(Tb4b4b4)
T8b949e##########################################################
T8b949e#### Program Startup #####################################
T8b949e##########################################################
T8b949e# This part of the program runs at startup,
T8b949e# and parses input of from the user, and then
T8b949e# starts up the desired program mode.
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3parser Tff7b72= Te6edf3argparseTff7b72.Td2a8ffArgumentParserTb4b4b4(Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffSimple link exampleTa5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff-sTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--serverTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstore_trueTa5d6ff"Tb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffwait for incoming link requests from clientsTa5d6ff"
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff--configTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffpath to alternative Reticulum config directoryTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ffdestinationTa5d6ff"Tb4b4b4,
Te6edf3nargsTff7b72=Ta5d6ff"Ta5d6ff?Ta5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffhexadecimal hash of the server destinationTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3args Tff7b72= Te6edf3parserTff7b72.Td2a8ffparse_argsTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3argsTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3configarg Tff7b72= Te6edf3argsTff7b72.Td2a8ffconfig
Tff7b72elseTb4b4b4:
Te6edf3configarg Tff7b72= Tff7b72None
Tff7b72if Te6edf3argsTff7b72.Td2a8ffserverTb4b4b4:
Te6edf3serverTb4b4b4(Te6edf3configargTb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72if Tb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestination Tff7b72== Tff7b72NoneTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffprint_helpTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3clientTb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestinationTb4b4b4, Te6edf3configargTb4b4b4)
Tff7b72except Tf85149KeyboardInterruptTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
This example can also be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Link.py.
Identification
The Identify example explores identifying an intiator of a link, once the link has been established.
T282828
T8b949e##########################################################
T8b949e# This RNS example demonstrates how to set up a link to #
T8b949e# a destination, and identify the initiator to it's peer #
T8b949e##########################################################
Tff7b72import T7ee787os
Tff7b72import T7ee787sys
Tff7b72import T7ee787time
Tff7b72import T7ee787argparse
Tff7b72import T7ee787RNS
T8b949e# Let's define an app name. We'll use this for all
T8b949e# destinations we create. Since this echo example
T8b949e# is part of a range of example utilities, we'll put
T8b949e# them all within the app namespace "example_utilities"
Te6edf3APP_NAME Tff7b72= Ta5d6ff"Ta5d6ffexample_utilitiesTa5d6ff"
T8b949e##########################################################
T8b949e#### Server Part #########################################
T8b949e##########################################################
T8b949e# A reference to the latest client link that connected
Te6edf3latest_client_link Tff7b72= Tff7b72None
T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a server
Tff7b72def Td2a8ffserverTb4b4b4(Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Randomly create a new identity for our link example
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTb4b4b4(Tb4b4b4)
T8b949e# We create a destination that clients can connect to. We
T8b949e# want clients to create links to this destination, so we
T8b949e# need to create a "single" destination type.
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffINTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffidentifyexampleTa5d6ff"
Tb4b4b4)
T8b949e# We configure a function that will get called every time
T8b949e# a new client creates a link to this destination.
Te6edf3server_destinationTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3client_connectedTb4b4b4)
T8b949e# Everything's ready!
T8b949e# Let's Wait for client requests or user input
Te6edf3server_loopTb4b4b4(Te6edf3server_destinationTb4b4b4)
Tff7b72def Td2a8ffserver_loopTb4b4b4(Te6edf3destinationTb4b4b4)Tb4b4b4:
T8b949e# Let the user know that everything is ready
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffLink identification example Ta5d6ff"Tff7b72+
Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tff7b72+
Ta5d6ff"Ta5d6ff running, waiting for a connection.Ta5d6ff"
Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffHit enter to manually send an announce (Ctrl-C to quit)Ta5d6ff"Tb4b4b4)
T8b949e# We enter a loop that runs until the users exits.
T8b949e# If the user hits enter, we will announce our server
T8b949e# destination on the network, which will let clients
T8b949e# know how to create messages directed towards it.
Tff7b72while Tff7b72TrueTb4b4b4:
Te6edf3entered Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
Te6edf3destinationTff7b72.Td2a8ffannounceTb4b4b4(Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffSent announce from Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tb4b4b4)
T8b949e# When a client establishes a link to our server
T8b949e# destination, this function will be called with
T8b949e# a reference to the link.
Tff7b72def Td2a8ffclient_connectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3latest_client_link
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient connectedTa5d6ff"Tb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3client_disconnectedTb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_packet_callbackTb4b4b4(Te6edf3server_packet_receivedTb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_remote_identified_callbackTb4b4b4(Te6edf3remote_identifiedTb4b4b4)
Te6edf3latest_client_link Tff7b72= Te6edf3link
Tff7b72def Td2a8ffclient_disconnectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient disconnectedTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffremote_identifiedTb4b4b4(Te6edf3linkTb4b4b4, Te6edf3identityTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffRemote identified as: Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3identityTb4b4b4)Tb4b4b4)
Tff7b72def Td2a8ffserver_packet_receivedTb4b4b4(Te6edf3messageTb4b4b4, Te6edf3packetTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3latest_client_link
T8b949e# Get the originating identity for display
Te6edf3remote_peer Tff7b72= Ta5d6ff"Ta5d6ffunidentified peerTa5d6ff"
Tff7b72if Te6edf3packetTff7b72.Td2a8fflinkTff7b72.Td2a8ffget_remote_identityTb4b4b4(Tb4b4b4) Tff7b72!= Tff7b72NoneTb4b4b4:
Te6edf3remote_peer Tff7b72= Tffa657strTb4b4b4(Te6edf3packetTff7b72.Td2a8fflinkTff7b72.Td2a8ffget_remote_identityTb4b4b4(Tb4b4b4)Tb4b4b4)
T8b949e# When data is received over any active link,
T8b949e# it will all be directed to the last client
T8b949e# that connected.
Te6edf3text Tff7b72= Te6edf3messageTff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffReceived data from Ta5d6ff"Tff7b72+Te6edf3remote_peerTff7b72+Ta5d6ff"Ta5d6ff: Ta5d6ff"Tff7b72+Te6edf3textTb4b4b4)
Te6edf3reply_text Tff7b72= Ta5d6ff"Ta5d6ffI received Tffea00\"Ta5d6ff"Tff7b72+Te6edf3textTff7b72+Ta5d6ff"Tffea00\"Ta5d6ff over the link from Ta5d6ff"Tff7b72+Te6edf3remote_peer
Te6edf3reply_data Tff7b72= Te6edf3reply_textTff7b72.Td2a8ffencodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8ffPacketTb4b4b4(Te6edf3latest_client_linkTb4b4b4, Te6edf3reply_dataTb4b4b4)Tff7b72.Td2a8ffsendTb4b4b4(Tb4b4b4)
T8b949e##########################################################
T8b949e#### Client Part #########################################
T8b949e##########################################################
T8b949e# A reference to the server link
Te6edf3server_link Tff7b72= Tff7b72None
T8b949e# A reference to the client identity
Te6edf3client_identity Tff7b72= Tff7b72None
T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a client
Tff7b72def Td2a8ffclientTb4b4b4(Te6edf3destination_hexhashTb4b4b4, Te6edf3configpathTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3client_identity
T8b949e# We need a binary representation of the destination
T8b949e# hash that was entered on the command line
Tff7b72tryTb4b4b4:
Te6edf3dest_len Tff7b72= Tb4b4b4(Te6edf3RNSTff7b72.Td2a8ffReticulumTff7b72.Td2a8ffTRUNCATED_HASHLENGTHTff7b72/Tff7b72/T79c0ff8Tb4b4b4)Tff7b72*T79c0ff2
Tff7b72if Tffa657lenTb4b4b4(Te6edf3destination_hexhashTb4b4b4) Tff7b72!= Te6edf3dest_lenTb4b4b4:
Tff7b72raise Tf85149ValueErrorTb4b4b4(
Ta5d6ff"Ta5d6ffDestination length is invalid, must be Tffd700{hex}Ta5d6ff hexadecimal characters (Tffd700{byte}Ta5d6ff bytes).Ta5d6ff"Tff7b72.Td2a8ffformatTb4b4b4(Tffa657hexTff7b72=Te6edf3dest_lenTb4b4b4, Te6edf3byteTff7b72=Te6edf3dest_lenTff7b72/Tff7b72/T79c0ff2Tb4b4b4)
Tb4b4b4)
Te6edf3destination_hash Tff7b72= Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Te6edf3destination_hexhashTb4b4b4)
Tff7b72exceptTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffInvalid destination entered. Check your input!Tffea00\nTa5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Create a new client identity
Te6edf3client_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTb4b4b4(Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffClient created new identity Ta5d6ff"Tff7b72+
Tffa657strTb4b4b4(Te6edf3client_identityTb4b4b4)
Tb4b4b4)
T8b949e# Check if we know a path to the destination
Tff7b72if Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffDestination is not yet known. Requesting path and waiting for announce to arrive...Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffrequest_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)
Tff7b72while Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)
T8b949e# Recall the server identity
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTff7b72.Td2a8ffrecallTb4b4b4(Te6edf3destination_hashTb4b4b4)
T8b949e# Inform the user that we'll begin connecting
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffEstablishing link with server...Ta5d6ff"Tb4b4b4)
T8b949e# When the server identity is known, we set
T8b949e# up a destination
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffOUTTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffidentifyexampleTa5d6ff"
Tb4b4b4)
T8b949e# And create a link
Te6edf3link Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTb4b4b4(Te6edf3server_destinationTb4b4b4)
T8b949e# We set a callback that will get executed
T8b949e# every time a packet is received over the
T8b949e# link
Te6edf3linkTff7b72.Td2a8ffset_packet_callbackTb4b4b4(Te6edf3client_packet_receivedTb4b4b4)
T8b949e# We'll also set up functions to inform the
T8b949e# user when the link is established or closed
Te6edf3linkTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3link_establishedTb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3link_closedTb4b4b4)
T8b949e# Everything is set up, so let's enter a loop
T8b949e# for the user to interact with the example
Te6edf3client_loopTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ffclient_loopTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3server_link
T8b949e# Wait for the link to become active
Tff7b72while Tff7b72not Te6edf3server_linkTb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)
Te6edf3should_quit Tff7b72= Tff7b72False
Tff7b72while Tff7b72not Te6edf3should_quitTb4b4b4:
Tff7b72tryTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff> Ta5d6ff"Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4)
Te6edf3text Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
T8b949e# Check if we should quit the example
Tff7b72if Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffquitTa5d6ff" Tff7b72or Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffqTa5d6ff" Tff7b72or Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffexitTa5d6ff"Tb4b4b4:
Te6edf3should_quit Tff7b72= Tff7b72True
Te6edf3server_linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
T8b949e# If not, send the entered text over the link
Tff7b72if Te6edf3text Tff7b72!= Ta5d6ff"Ta5d6ff"Tb4b4b4:
Te6edf3data Tff7b72= Te6edf3textTff7b72.Td2a8ffencodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Tff7b72if Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4) Tff7b72<Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffMDUTb4b4b4:
Te6edf3RNSTff7b72.Td2a8ffPacketTb4b4b4(Te6edf3server_linkTb4b4b4, Te6edf3dataTb4b4b4)Tff7b72.Td2a8ffsendTb4b4b4(Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffCannot send this packet, the data size of Ta5d6ff"Tff7b72+
Tffa657strTb4b4b4(Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4)Tb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff bytes exceeds the link packet MDU of Ta5d6ff"Tff7b72+
Tffa657strTb4b4b4(Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffMDUTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff bytesTa5d6ff"Tb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffLOG_ERROR
Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffError while sending data over the link: Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4)
Te6edf3should_quit Tff7b72= Tff7b72True
Te6edf3server_linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
T8b949e# This function is called when a link
T8b949e# has been established with the server
Tff7b72def Td2a8fflink_establishedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
T8b949e# We store a reference to the link
T8b949e# instance for later use
Tff7b72global Te6edf3server_linkTb4b4b4, Te6edf3client_identity
Te6edf3server_link Tff7b72= Te6edf3link
T8b949e# Inform the user that the server is
T8b949e# connected
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink established with server, identifying to remote peer...Ta5d6ff"Tb4b4b4)
Te6edf3linkTff7b72.Td2a8ffidentifyTb4b4b4(Te6edf3client_identityTb4b4b4)
T8b949e# When a link is closed, we'll inform the
T8b949e# user, and exit the program
Tff7b72def Td2a8fflink_closedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffTIMEOUTTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link timed out, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elif Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffDESTINATION_CLOSEDTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link was closed by the server, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink closed, exiting nowTa5d6ff"Tb4b4b4)
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff1.5Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
T8b949e# When a packet is received over the link, we
T8b949e# simply print out the data.
Tff7b72def Td2a8ffclient_packet_receivedTb4b4b4(Te6edf3messageTb4b4b4, Te6edf3packetTb4b4b4)Tb4b4b4:
Te6edf3text Tff7b72= Te6edf3messageTff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffReceived data on the link: Ta5d6ff"Tff7b72+Te6edf3textTb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff> Ta5d6ff"Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffstdoutTff7b72.Td2a8ffflushTb4b4b4(Tb4b4b4)
T8b949e##########################################################
T8b949e#### Program Startup #####################################
T8b949e##########################################################
T8b949e# This part of the program runs at startup,
T8b949e# and parses input of from the user, and then
T8b949e# starts up the desired program mode.
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3parser Tff7b72= Te6edf3argparseTff7b72.Td2a8ffArgumentParserTb4b4b4(Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffSimple link exampleTa5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff-sTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--serverTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstore_trueTa5d6ff"Tb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffwait for incoming link requests from clientsTa5d6ff"
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff--configTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffpath to alternative Reticulum config directoryTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ffdestinationTa5d6ff"Tb4b4b4,
Te6edf3nargsTff7b72=Ta5d6ff"Ta5d6ff?Ta5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffhexadecimal hash of the server destinationTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3args Tff7b72= Te6edf3parserTff7b72.Td2a8ffparse_argsTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3argsTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3configarg Tff7b72= Te6edf3argsTff7b72.Td2a8ffconfig
Tff7b72elseTb4b4b4:
Te6edf3configarg Tff7b72= Tff7b72None
Tff7b72if Te6edf3argsTff7b72.Td2a8ffserverTb4b4b4:
Te6edf3serverTb4b4b4(Te6edf3configargTb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72if Tb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestination Tff7b72== Tff7b72NoneTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffprint_helpTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3clientTb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestinationTb4b4b4, Te6edf3configargTb4b4b4)
Tff7b72except Tf85149KeyboardInterruptTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
This example can also be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Identify.py.
Requests & Responses
The Request example explores sending requests and receiving responses.
T282828
T8b949e##########################################################
T8b949e# This RNS example demonstrates how to perform requests #
T8b949e# and receive responses over a link. #
T8b949e##########################################################
Tff7b72import T7ee787os
Tff7b72import T7ee787sys
Tff7b72import T7ee787time
Tff7b72import T7ee787random
Tff7b72import T7ee787argparse
Tff7b72import T7ee787RNS
T8b949e# Let's define an app name. We'll use this for all
T8b949e# destinations we create. Since this echo example
T8b949e# is part of a range of example utilities, we'll put
T8b949e# them all within the app namespace "example_utilities"
Te6edf3APP_NAME Tff7b72= Ta5d6ff"Ta5d6ffexample_utilitiesTa5d6ff"
T8b949e##########################################################
T8b949e#### Server Part #########################################
T8b949e##########################################################
T8b949e# A reference to the latest client link that connected
Te6edf3latest_client_link Tff7b72= Tff7b72None
Tff7b72def Td2a8ffrandom_text_generatorTb4b4b4(Te6edf3pathTb4b4b4, Te6edf3dataTb4b4b4, Te6edf3request_idTb4b4b4, Te6edf3link_idTb4b4b4, Te6edf3remote_identityTb4b4b4, Te6edf3requested_atTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffGenerating response to request Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3request_idTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff on link Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3link_idTb4b4b4)Tb4b4b4)
Te6edf3texts Tff7b72= Tb4b4b4[Ta5d6ff"Ta5d6ffThey looked upTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffOn each full moonTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffBecky was upsetTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffI’ll stay away from itTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffThe pet shop stocks everythingTa5d6ff"Tb4b4b4]
Tff7b72return Te6edf3textsTb4b4b4[Te6edf3randomTff7b72.Td2a8ffrandintTb4b4b4(T79c0ff0Tb4b4b4, Tffa657lenTb4b4b4(Te6edf3textsTb4b4b4)Tff7b72-T79c0ff1Tb4b4b4)Tb4b4b4]
T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a server
Tff7b72def Td2a8ffserverTb4b4b4(Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Randomly create a new identity for our link example
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTb4b4b4(Tb4b4b4)
T8b949e# We create a destination that clients can connect to. We
T8b949e# want clients to create links to this destination, so we
T8b949e# need to create a "single" destination type.
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffINTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffrequestexampleTa5d6ff"
Tb4b4b4)
T8b949e# We configure a function that will get called every time
T8b949e# a new client creates a link to this destination.
Te6edf3server_destinationTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3client_connectedTb4b4b4)
T8b949e# We register a request handler for handling incoming
T8b949e# requests over any established links.
Te6edf3server_destinationTff7b72.Td2a8ffregister_request_handlerTb4b4b4(
Ta5d6ff"Ta5d6ff/random/textTa5d6ff"Tb4b4b4,
Te6edf3response_generator Tff7b72= Te6edf3random_text_generatorTb4b4b4,
Te6edf3allow Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffALLOW_ALL
Tb4b4b4)
T8b949e# Everything's ready!
T8b949e# Let's Wait for client requests or user input
Te6edf3server_loopTb4b4b4(Te6edf3server_destinationTb4b4b4)
Tff7b72def Td2a8ffserver_loopTb4b4b4(Te6edf3destinationTb4b4b4)Tb4b4b4:
T8b949e# Let the user know that everything is ready
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffRequest example Ta5d6ff"Tff7b72+
Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tff7b72+
Ta5d6ff"Ta5d6ff running, waiting for a connection.Ta5d6ff"
Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffHit enter to manually send an announce (Ctrl-C to quit)Ta5d6ff"Tb4b4b4)
T8b949e# We enter a loop that runs until the users exits.
T8b949e# If the user hits enter, we will announce our server
T8b949e# destination on the network, which will let clients
T8b949e# know how to create messages directed towards it.
Tff7b72while Tff7b72TrueTb4b4b4:
Te6edf3entered Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
Te6edf3destinationTff7b72.Td2a8ffannounceTb4b4b4(Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffSent announce from Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tb4b4b4)
T8b949e# When a client establishes a link to our server
T8b949e# destination, this function will be called with
T8b949e# a reference to the link.
Tff7b72def Td2a8ffclient_connectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3latest_client_link
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient connectedTa5d6ff"Tb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3client_disconnectedTb4b4b4)
Te6edf3latest_client_link Tff7b72= Te6edf3link
Tff7b72def Td2a8ffclient_disconnectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient disconnectedTa5d6ff"Tb4b4b4)
T8b949e##########################################################
T8b949e#### Client Part #########################################
T8b949e##########################################################
T8b949e# A reference to the server link
Te6edf3server_link Tff7b72= Tff7b72None
T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a client
Tff7b72def Td2a8ffclientTb4b4b4(Te6edf3destination_hexhashTb4b4b4, Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We need a binary representation of the destination
T8b949e# hash that was entered on the command line
Tff7b72tryTb4b4b4:
Te6edf3dest_len Tff7b72= Tb4b4b4(Te6edf3RNSTff7b72.Td2a8ffReticulumTff7b72.Td2a8ffTRUNCATED_HASHLENGTHTff7b72/Tff7b72/T79c0ff8Tb4b4b4)Tff7b72*T79c0ff2
Tff7b72if Tffa657lenTb4b4b4(Te6edf3destination_hexhashTb4b4b4) Tff7b72!= Te6edf3dest_lenTb4b4b4:
Tff7b72raise Tf85149ValueErrorTb4b4b4(
Ta5d6ff"Ta5d6ffDestination length is invalid, must be Tffd700{hex}Ta5d6ff hexadecimal characters (Tffd700{byte}Ta5d6ff bytes).Ta5d6ff"Tff7b72.Td2a8ffformatTb4b4b4(Tffa657hexTff7b72=Te6edf3dest_lenTb4b4b4, Te6edf3byteTff7b72=Te6edf3dest_lenTff7b72/Tff7b72/T79c0ff2Tb4b4b4)
Tb4b4b4)
Te6edf3destination_hash Tff7b72= Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Te6edf3destination_hexhashTb4b4b4)
Tff7b72exceptTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffInvalid destination entered. Check your input!Tffea00\nTa5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Check if we know a path to the destination
Tff7b72if Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffDestination is not yet known. Requesting path and waiting for announce to arrive...Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffrequest_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)
Tff7b72while Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)
T8b949e# Recall the server identity
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTff7b72.Td2a8ffrecallTb4b4b4(Te6edf3destination_hashTb4b4b4)
T8b949e# Inform the user that we'll begin connecting
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffEstablishing link with server...Ta5d6ff"Tb4b4b4)
T8b949e# When the server identity is known, we set
T8b949e# up a destination
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffOUTTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffrequestexampleTa5d6ff"
Tb4b4b4)
T8b949e# And create a link
Te6edf3link Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTb4b4b4(Te6edf3server_destinationTb4b4b4)
T8b949e# We'll set up functions to inform the
T8b949e# user when the link is established or closed
Te6edf3linkTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3link_establishedTb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3link_closedTb4b4b4)
T8b949e# Everything is set up, so let's enter a loop
T8b949e# for the user to interact with the example
Te6edf3client_loopTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ffclient_loopTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3server_link
T8b949e# Wait for the link to become active
Tff7b72while Tff7b72not Te6edf3server_linkTb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)
Te6edf3should_quit Tff7b72= Tff7b72False
Tff7b72while Tff7b72not Te6edf3should_quitTb4b4b4:
Tff7b72tryTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff> Ta5d6ff"Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4)
Te6edf3text Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
T8b949e# Check if we should quit the example
Tff7b72if Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffquitTa5d6ff" Tff7b72or Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffqTa5d6ff" Tff7b72or Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffexitTa5d6ff"Tb4b4b4:
Te6edf3should_quit Tff7b72= Tff7b72True
Te6edf3server_linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3server_linkTff7b72.Td2a8ffrequestTb4b4b4(
Ta5d6ff"Ta5d6ff/random/textTa5d6ff"Tb4b4b4,
Te6edf3data Tff7b72= Tff7b72NoneTb4b4b4,
Te6edf3response_callback Tff7b72= Te6edf3got_responseTb4b4b4,
Te6edf3failed_callback Tff7b72= Te6edf3request_failed
Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffError while sending request over the link: Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4)
Te6edf3should_quit Tff7b72= Tff7b72True
Te6edf3server_linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ffgot_responseTb4b4b4(Te6edf3request_receiptTb4b4b4)Tb4b4b4:
Te6edf3request_id Tff7b72= Te6edf3request_receiptTff7b72.Td2a8ffrequest_id
Te6edf3response Tff7b72= Te6edf3request_receiptTff7b72.Td2a8ffresponse
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffGot response for request Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3request_idTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff: Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3responseTb4b4b4)Tb4b4b4)
Tff7b72def Td2a8ffrequest_receivedTb4b4b4(Te6edf3request_receiptTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe request Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3request_receiptTff7b72.Td2a8ffrequest_idTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff was received by the remote peer.Ta5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffrequest_failedTb4b4b4(Te6edf3request_receiptTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe request Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3request_receiptTff7b72.Td2a8ffrequest_idTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff failed.Ta5d6ff"Tb4b4b4)
T8b949e# This function is called when a link
T8b949e# has been established with the server
Tff7b72def Td2a8fflink_establishedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
T8b949e# We store a reference to the link
T8b949e# instance for later use
Tff7b72global Te6edf3server_link
Te6edf3server_link Tff7b72= Te6edf3link
T8b949e# Inform the user that the server is
T8b949e# connected
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink established with server, hit enter to perform a request, or type in Tffea00\"Ta5d6ffquitTffea00\"Ta5d6ff to quitTa5d6ff"Tb4b4b4)
T8b949e# When a link is closed, we'll inform the
T8b949e# user, and exit the program
Tff7b72def Td2a8fflink_closedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffTIMEOUTTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link timed out, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elif Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffDESTINATION_CLOSEDTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link was closed by the server, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink closed, exiting nowTa5d6ff"Tb4b4b4)
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff1.5Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
T8b949e##########################################################
T8b949e#### Program Startup #####################################
T8b949e##########################################################
T8b949e# This part of the program runs at startup,
T8b949e# and parses input of from the user, and then
T8b949e# starts up the desired program mode.
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3parser Tff7b72= Te6edf3argparseTff7b72.Td2a8ffArgumentParserTb4b4b4(Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffSimple request/response exampleTa5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff-sTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--serverTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstore_trueTa5d6ff"Tb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffwait for incoming requests from clientsTa5d6ff"
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff--configTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffpath to alternative Reticulum config directoryTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ffdestinationTa5d6ff"Tb4b4b4,
Te6edf3nargsTff7b72=Ta5d6ff"Ta5d6ff?Ta5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffhexadecimal hash of the server destinationTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3args Tff7b72= Te6edf3parserTff7b72.Td2a8ffparse_argsTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3argsTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3configarg Tff7b72= Te6edf3argsTff7b72.Td2a8ffconfig
Tff7b72elseTb4b4b4:
Te6edf3configarg Tff7b72= Tff7b72None
Tff7b72if Te6edf3argsTff7b72.Td2a8ffserverTb4b4b4:
Te6edf3serverTb4b4b4(Te6edf3configargTb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72if Tb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestination Tff7b72== Tff7b72NoneTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffprint_helpTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3clientTb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestinationTb4b4b4, Te6edf3configargTb4b4b4)
Tff7b72except Tf85149KeyboardInterruptTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
This example can also be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Request.py.
Channel
The Channel example explores using a T383838Channel to send structured data between peers of a T383838Link.
T282828
T8b949e##########################################################
T8b949e# This RNS example demonstrates how to set up a link to #
T8b949e# a destination, and pass structured messages over it #
T8b949e# using a channel. #
T8b949e##########################################################
Tff7b72import T7ee787os
Tff7b72import T7ee787sys
Tff7b72import T7ee787time
Tff7b72import T7ee787argparse
Tff7b72from T7ee787datetime Tff7b72import Te6edf3datetime
Tff7b72import T7ee787RNS
Tff7b72from T7ee787RNST7ee787.T7ee787vendor Tff7b72import Te6edf3umsgpack
T8b949e# Let's define an app name. We'll use this for all
T8b949e# destinations we create. Since this echo example
T8b949e# is part of a range of example utilities, we'll put
T8b949e# them all within the app namespace "example_utilities"
Te6edf3APP_NAME Tff7b72= Ta5d6ff"Ta5d6ffexample_utilitiesTa5d6ff"
T8b949e##########################################################
T8b949e#### Shared Objects ######################################
T8b949e##########################################################
T8b949e# Channel data must be structured in a subclass of
T8b949e# MessageBase. This ensures that the channel will be able
T8b949e# to serialize and deserialize the object and multiplex it
T8b949e# with other objects. Both ends of a link will need the
T8b949e# same object definitions to be able to communicate over
T8b949e# a channel.
T8b949e#
T8b949e# Note: The objects we wish to use over the channel must
T8b949e# be registered with the channel, and each link has a
T8b949e# different channel instance. See the client_connected
T8b949e# and link_established functions in this example to see
T8b949e# how message types are registered.
T8b949e# Let's make a simple message class called StringMessage
T8b949e# that will convey a string with a timestamp.
Tff7b72class T56d364StringMessageTb4b4b4(Te6edf3RNSTff7b72.Td2a8ffMessageBaseTb4b4b4)Tb4b4b4:
T8b949e# The MSGTYPE class variable needs to be assigned a
T8b949e# 2 byte integer value. This identifier allows the
T8b949e# channel to look up your message's constructor when a
T8b949e# message arrives over the channel.
T8b949e#
T8b949e# MSGTYPE must be unique across all message types we
T8b949e# register with the channel. MSGTYPEs >= 0xf000 are
T8b949e# reserved for the system.
Te6edf3MSGTYPE Tff7b72= T79c0ff0x0101
T8b949e# The constructor of our object must be callable with
T8b949e# no arguments. We can have parameters, but they must
T8b949e# have a default assignment.
T8b949e#
T8b949e# This is needed so the channel can create an empty
T8b949e# version of our message into which the incoming
T8b949e# message can be unpacked.
Tff7b72def Tff7b72__init__Tb4b4b4(Tff7b72selfTb4b4b4, Te6edf3dataTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffdata Tff7b72= Te6edf3data
Tff7b72selfTff7b72.Td2a8fftimestamp Tff7b72= Te6edf3datetimeTff7b72.Td2a8ffnowTb4b4b4(Tb4b4b4)
T8b949e# Finally, our message needs to implement functions
T8b949e# the channel can call to pack and unpack our message
T8b949e# to/from the raw packet payload. We'll use the
T8b949e# umsgpack package bundled with RNS. We could also use
T8b949e# the struct package bundled with Python if we wanted
T8b949e# more control over the structure of the packed bytes.
T8b949e#
T8b949e# Also note that packed message objects must fit
T8b949e# entirely in one packet. The number of bytes
T8b949e# available for message payloads can be queried from
T8b949e# the channel using the Channel.MDU property. The
T8b949e# channel MDU is slightly less than the link MDU due
T8b949e# to encoding the message header.
T8b949e# The pack function encodes the message contents into
T8b949e# a byte stream.
Tff7b72def Td2a8ffpackTb4b4b4(Tff7b72selfTb4b4b4) Tff7b72-Tff7b72> Tffa657bytesTb4b4b4:
Tff7b72return Te6edf3umsgpackTff7b72.Td2a8ffpackbTb4b4b4(Tb4b4b4(Tff7b72selfTff7b72.Td2a8ffdataTb4b4b4, Tff7b72selfTff7b72.Td2a8fftimestampTb4b4b4)Tb4b4b4)
T8b949e# And the unpack function decodes a byte stream into
T8b949e# the message contents.
Tff7b72def Td2a8ffunpackTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3rawTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffdataTb4b4b4, Tff7b72selfTff7b72.Td2a8fftimestamp Tff7b72= Te6edf3umsgpackTff7b72.Td2a8ffunpackbTb4b4b4(Te6edf3rawTb4b4b4)
T8b949e##########################################################
T8b949e#### Server Part #########################################
T8b949e##########################################################
T8b949e# A reference to the latest client link that connected
Te6edf3latest_client_link Tff7b72= Tff7b72None
T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a server
Tff7b72def Td2a8ffserverTb4b4b4(Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Randomly create a new identity for our link example
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTb4b4b4(Tb4b4b4)
T8b949e# We create a destination that clients can connect to. We
T8b949e# want clients to create links to this destination, so we
T8b949e# need to create a "single" destination type.
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffINTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffchannelexampleTa5d6ff"
Tb4b4b4)
T8b949e# We configure a function that will get called every time
T8b949e# a new client creates a link to this destination.
Te6edf3server_destinationTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3client_connectedTb4b4b4)
T8b949e# Everything's ready!
T8b949e# Let's Wait for client requests or user input
Te6edf3server_loopTb4b4b4(Te6edf3server_destinationTb4b4b4)
Tff7b72def Td2a8ffserver_loopTb4b4b4(Te6edf3destinationTb4b4b4)Tb4b4b4:
T8b949e# Let the user know that everything is ready
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffChannel example Ta5d6ff"Tff7b72+
Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tff7b72+
Ta5d6ff"Ta5d6ff running, waiting for a connection.Ta5d6ff"
Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffHit enter to manually send an announce (Ctrl-C to quit)Ta5d6ff"Tb4b4b4)
T8b949e# We enter a loop that runs until the users exits.
T8b949e# If the user hits enter, we will announce our server
T8b949e# destination on the network, which will let clients
T8b949e# know how to create messages directed towards it.
Tff7b72while Tff7b72TrueTb4b4b4:
Te6edf3entered Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
Te6edf3destinationTff7b72.Td2a8ffannounceTb4b4b4(Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffSent announce from Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tb4b4b4)
T8b949e# When a client establishes a link to our server
T8b949e# destination, this function will be called with
T8b949e# a reference to the link.
Tff7b72def Td2a8ffclient_connectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3latest_client_link
Te6edf3latest_client_link Tff7b72= Te6edf3link
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient connectedTa5d6ff"Tb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3client_disconnectedTb4b4b4)
T8b949e# Register message types and add callback to channel
Te6edf3channel Tff7b72= Te6edf3linkTff7b72.Td2a8ffget_channelTb4b4b4(Tb4b4b4)
Te6edf3channelTff7b72.Td2a8ffregister_message_typeTb4b4b4(Te6edf3StringMessageTb4b4b4)
Te6edf3channelTff7b72.Td2a8ffadd_message_handlerTb4b4b4(Te6edf3server_message_receivedTb4b4b4)
Tff7b72def Td2a8ffclient_disconnectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient disconnectedTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffserver_message_receivedTb4b4b4(Te6edf3messageTb4b4b4)Tb4b4b4:
T8b949e"""
A message handler
@param message: An instance of a subclass of MessageBase
@return: True if message was handled
"""
Tff7b72global Te6edf3latest_client_link
T8b949e# When a message is received over any active link,
T8b949e# the replies will all be directed to the last client
T8b949e# that connected.
T8b949e# In a message handler, any deserializable message
T8b949e# that arrives over the link's channel will be passed
T8b949e# to all message handlers, unless a preceding handler indicates it
T8b949e# has handled the message.
T8b949e#
T8b949e#
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3messageTb4b4b4, Te6edf3StringMessageTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffReceived data on the link: Ta5d6ff" Tff7b72+ Te6edf3messageTff7b72.Td2a8ffdata Tff7b72+ Ta5d6ff"Ta5d6ff (message created at Ta5d6ff" Tff7b72+ Tffa657strTb4b4b4(Te6edf3messageTff7b72.Td2a8fftimestampTb4b4b4) Tff7b72+ Ta5d6ff"Ta5d6ff)Ta5d6ff"Tb4b4b4)
Te6edf3reply_message Tff7b72= Te6edf3StringMessageTb4b4b4(Ta5d6ff"Ta5d6ffI received Tffea00\"Ta5d6ff"Tff7b72+Te6edf3messageTff7b72.Td2a8ffdataTff7b72+Ta5d6ff"Tffea00\"Ta5d6ff over the linkTa5d6ff"Tb4b4b4)
Te6edf3latest_client_linkTff7b72.Td2a8ffget_channelTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffsendTb4b4b4(Te6edf3reply_messageTb4b4b4)
T8b949e# Incoming messages are sent to each message
T8b949e# handler added to the channel, in the order they
T8b949e# were added.
T8b949e# If any message handler returns True, the message
T8b949e# is considered handled and any subsequent
T8b949e# handlers are skipped.
Tff7b72return Tff7b72True
T8b949e##########################################################
T8b949e#### Client Part #########################################
T8b949e##########################################################
T8b949e# A reference to the server link
Te6edf3server_link Tff7b72= Tff7b72None
T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a client
Tff7b72def Td2a8ffclientTb4b4b4(Te6edf3destination_hexhashTb4b4b4, Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We need a binary representation of the destination
T8b949e# hash that was entered on the command line
Tff7b72tryTb4b4b4:
Te6edf3dest_len Tff7b72= Tb4b4b4(Te6edf3RNSTff7b72.Td2a8ffReticulumTff7b72.Td2a8ffTRUNCATED_HASHLENGTHTff7b72/Tff7b72/T79c0ff8Tb4b4b4)Tff7b72*T79c0ff2
Tff7b72if Tffa657lenTb4b4b4(Te6edf3destination_hexhashTb4b4b4) Tff7b72!= Te6edf3dest_lenTb4b4b4:
Tff7b72raise Tf85149ValueErrorTb4b4b4(
Ta5d6ff"Ta5d6ffDestination length is invalid, must be Tffd700{hex}Ta5d6ff hexadecimal characters (Tffd700{byte}Ta5d6ff bytes).Ta5d6ff"Tff7b72.Td2a8ffformatTb4b4b4(Tffa657hexTff7b72=Te6edf3dest_lenTb4b4b4, Te6edf3byteTff7b72=Te6edf3dest_lenTff7b72/Tff7b72/T79c0ff2Tb4b4b4)
Tb4b4b4)
Te6edf3destination_hash Tff7b72= Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Te6edf3destination_hexhashTb4b4b4)
Tff7b72exceptTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffInvalid destination entered. Check your input!Tffea00\nTa5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Check if we know a path to the destination
Tff7b72if Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffDestination is not yet known. Requesting path and waiting for announce to arrive...Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffrequest_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)
Tff7b72while Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)
T8b949e# Recall the server identity
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTff7b72.Td2a8ffrecallTb4b4b4(Te6edf3destination_hashTb4b4b4)
T8b949e# Inform the user that we'll begin connecting
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffEstablishing link with server...Ta5d6ff"Tb4b4b4)
T8b949e# When the server identity is known, we set
T8b949e# up a destination
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffOUTTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffchannelexampleTa5d6ff"
Tb4b4b4)
T8b949e# And create a link
Te6edf3link Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTb4b4b4(Te6edf3server_destinationTb4b4b4)
T8b949e# We'll also set up functions to inform the
T8b949e# user when the link is established or closed
Te6edf3linkTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3link_establishedTb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3link_closedTb4b4b4)
T8b949e# Everything is set up, so let's enter a loop
T8b949e# for the user to interact with the example
Te6edf3client_loopTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ffclient_loopTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3server_link
T8b949e# Wait for the link to become active
Tff7b72while Tff7b72not Te6edf3server_linkTb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)
Te6edf3should_quit Tff7b72= Tff7b72False
Tff7b72while Tff7b72not Te6edf3should_quitTb4b4b4:
Tff7b72tryTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff> Ta5d6ff"Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4)
Te6edf3text Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
T8b949e# Check if we should quit the example
Tff7b72if Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffquitTa5d6ff" Tff7b72or Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffqTa5d6ff" Tff7b72or Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffexitTa5d6ff"Tb4b4b4:
Te6edf3should_quit Tff7b72= Tff7b72True
Te6edf3server_linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
T8b949e# If not, send the entered text over the link
Tff7b72if Te6edf3text Tff7b72!= Ta5d6ff"Ta5d6ff"Tb4b4b4:
Te6edf3message Tff7b72= Te6edf3StringMessageTb4b4b4(Te6edf3textTb4b4b4)
Te6edf3packed_size Tff7b72= Tffa657lenTb4b4b4(Te6edf3messageTff7b72.Td2a8ffpackTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3channel Tff7b72= Te6edf3server_linkTff7b72.Td2a8ffget_channelTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3channelTff7b72.Td2a8ffis_ready_to_sendTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3packed_size Tff7b72<Tff7b72= Te6edf3channelTff7b72.Td2a8ffmduTb4b4b4:
Te6edf3channelTff7b72.Td2a8ffsendTb4b4b4(Te6edf3messageTb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffCannot send this packet, the data size of Ta5d6ff"Tff7b72+
Tffa657strTb4b4b4(Te6edf3packed_sizeTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff bytes exceeds the link packet MDU of Ta5d6ff"Tff7b72+
Tffa657strTb4b4b4(Te6edf3channelTff7b72.Td2a8ffMDUTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff bytesTa5d6ff"Tb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffLOG_ERROR
Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffChannel is not ready to send, please wait for Ta5d6ff" Tff7b72+
Ta5d6ff"Ta5d6ffpending messages to complete.Ta5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffError while sending data over the link: Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4)
Te6edf3should_quit Tff7b72= Tff7b72True
Te6edf3server_linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
T8b949e# This function is called when a link
T8b949e# has been established with the server
Tff7b72def Td2a8fflink_establishedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
T8b949e# We store a reference to the link
T8b949e# instance for later use
Tff7b72global Te6edf3server_link
Te6edf3server_link Tff7b72= Te6edf3link
T8b949e# Register messages and add handler to channel
Te6edf3channel Tff7b72= Te6edf3linkTff7b72.Td2a8ffget_channelTb4b4b4(Tb4b4b4)
Te6edf3channelTff7b72.Td2a8ffregister_message_typeTb4b4b4(Te6edf3StringMessageTb4b4b4)
Te6edf3channelTff7b72.Td2a8ffadd_message_handlerTb4b4b4(Te6edf3client_message_receivedTb4b4b4)
T8b949e# Inform the user that the server is
T8b949e# connected
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink established with server, enter some text to send, or Tffea00\"Ta5d6ffquitTffea00\"Ta5d6ff to quitTa5d6ff"Tb4b4b4)
T8b949e# When a link is closed, we'll inform the
T8b949e# user, and exit the program
Tff7b72def Td2a8fflink_closedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffTIMEOUTTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link timed out, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elif Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffDESTINATION_CLOSEDTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link was closed by the server, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink closed, exiting nowTa5d6ff"Tb4b4b4)
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff1.5Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
T8b949e# When a packet is received over the channel, we
T8b949e# simply print out the data.
Tff7b72def Td2a8ffclient_message_receivedTb4b4b4(Te6edf3messageTb4b4b4)Tb4b4b4:
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3messageTb4b4b4, Te6edf3StringMessageTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffReceived data on the link: Ta5d6ff" Tff7b72+ Te6edf3messageTff7b72.Td2a8ffdata Tff7b72+ Ta5d6ff"Ta5d6ff (message created at Ta5d6ff" Tff7b72+ Tffa657strTb4b4b4(Te6edf3messageTff7b72.Td2a8fftimestampTb4b4b4) Tff7b72+ Ta5d6ff"Ta5d6ff)Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff> Ta5d6ff"Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffstdoutTff7b72.Td2a8ffflushTb4b4b4(Tb4b4b4)
T8b949e##########################################################
T8b949e#### Program Startup #####################################
T8b949e##########################################################
T8b949e# This part of the program runs at startup,
T8b949e# and parses input of from the user, and then
T8b949e# starts up the desired program mode.
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3parser Tff7b72= Te6edf3argparseTff7b72.Td2a8ffArgumentParserTb4b4b4(Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffSimple channel exampleTa5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff-sTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--serverTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstore_trueTa5d6ff"Tb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffwait for incoming link requests from clientsTa5d6ff"
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff--configTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffpath to alternative Reticulum config directoryTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ffdestinationTa5d6ff"Tb4b4b4,
Te6edf3nargsTff7b72=Ta5d6ff"Ta5d6ff?Ta5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffhexadecimal hash of the server destinationTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3args Tff7b72= Te6edf3parserTff7b72.Td2a8ffparse_argsTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3argsTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3configarg Tff7b72= Te6edf3argsTff7b72.Td2a8ffconfig
Tff7b72elseTb4b4b4:
Te6edf3configarg Tff7b72= Tff7b72None
Tff7b72if Te6edf3argsTff7b72.Td2a8ffserverTb4b4b4:
Te6edf3serverTb4b4b4(Te6edf3configargTb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72if Tb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestination Tff7b72== Tff7b72NoneTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffprint_helpTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3clientTb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestinationTb4b4b4, Te6edf3configargTb4b4b4)
Tff7b72except Tf85149KeyboardInterruptTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
This example can also be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Channel.py.
Buffer
The Buffer example explores using buffered readers and writers to send binary data between peers of a T383838Link.
T282828
T8b949e##########################################################
T8b949e# This RNS example demonstrates how to set up a link to #
T8b949e# a destination, and pass binary data over it using a #
T8b949e# channel buffer. #
T8b949e##########################################################
Tff7b72from T7ee787__future__ Tff7b72import Te6edf3annotations
Tff7b72import T7ee787os
Tff7b72import T7ee787sys
Tff7b72import T7ee787time
Tff7b72import T7ee787argparse
Tff7b72from T7ee787datetime Tff7b72import Te6edf3datetime
Tff7b72import T7ee787RNS
Tff7b72from T7ee787RNST7ee787.T7ee787vendor Tff7b72import Te6edf3umsgpack
T8b949e# Let's define an app name. We'll use this for all
T8b949e# destinations we create. Since this echo example
T8b949e# is part of a range of example utilities, we'll put
T8b949e# them all within the app namespace "example_utilities"
Te6edf3APP_NAME Tff7b72= Ta5d6ff"Ta5d6ffexample_utilitiesTa5d6ff"
T8b949e##########################################################
T8b949e#### Server Part #########################################
T8b949e##########################################################
T8b949e# A reference to the latest client link that connected
Te6edf3latest_client_link Tff7b72= Tff7b72None
T8b949e# A reference to the latest buffer object
Te6edf3latest_buffer Tff7b72= Tff7b72None
T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a server
Tff7b72def Td2a8ffserverTb4b4b4(Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Randomly create a new identity for our example
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTb4b4b4(Tb4b4b4)
T8b949e# We create a destination that clients can connect to. We
T8b949e# want clients to create links to this destination, so we
T8b949e# need to create a "single" destination type.
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffINTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffbufferexampleTa5d6ff"
Tb4b4b4)
T8b949e# We configure a function that will get called every time
T8b949e# a new client creates a link to this destination.
Te6edf3server_destinationTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3client_connectedTb4b4b4)
T8b949e# Everything's ready!
T8b949e# Let's Wait for client requests or user input
Te6edf3server_loopTb4b4b4(Te6edf3server_destinationTb4b4b4)
Tff7b72def Td2a8ffserver_loopTb4b4b4(Te6edf3destinationTb4b4b4)Tb4b4b4:
T8b949e# Let the user know that everything is ready
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffLink buffer example Ta5d6ff"Tff7b72+
Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tff7b72+
Ta5d6ff"Ta5d6ff running, waiting for a connection.Ta5d6ff"
Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffHit enter to manually send an announce (Ctrl-C to quit)Ta5d6ff"Tb4b4b4)
T8b949e# We enter a loop that runs until the users exits.
T8b949e# If the user hits enter, we will announce our server
T8b949e# destination on the network, which will let clients
T8b949e# know how to create messages directed towards it.
Tff7b72while Tff7b72TrueTb4b4b4:
Te6edf3entered Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
Te6edf3destinationTff7b72.Td2a8ffannounceTb4b4b4(Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffSent announce from Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tb4b4b4)
T8b949e# When a client establishes a link to our server
T8b949e# destination, this function will be called with
T8b949e# a reference to the link.
Tff7b72def Td2a8ffclient_connectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3latest_client_linkTb4b4b4, Te6edf3latest_buffer
Te6edf3latest_client_link Tff7b72= Te6edf3link
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient connectedTa5d6ff"Tb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3client_disconnectedTb4b4b4)
T8b949e# If a new connection is received, the old reader
T8b949e# needs to be disconnected.
Tff7b72if Te6edf3latest_bufferTb4b4b4:
Te6edf3latest_bufferTff7b72.Td2a8ffcloseTb4b4b4(Tb4b4b4)
T8b949e# Create buffer objects.
T8b949e# The stream_id parameter to these functions is
T8b949e# a bit like a file descriptor, except that it
T8b949e# is unique to the *receiver*.
T8b949e#
T8b949e# In this example, both the reader and the writer
T8b949e# use stream_id = 0, but there are actually two
T8b949e# separate unidirectional streams flowing in
T8b949e# opposite directions.
T8b949e#
Te6edf3channel Tff7b72= Te6edf3linkTff7b72.Td2a8ffget_channelTb4b4b4(Tb4b4b4)
Te6edf3latest_buffer Tff7b72= Te6edf3RNSTff7b72.Td2a8ffBufferTff7b72.Td2a8ffcreate_bidirectional_bufferTb4b4b4(T79c0ff0Tb4b4b4, T79c0ff0Tb4b4b4, Te6edf3channelTb4b4b4, Te6edf3server_buffer_readyTb4b4b4)
Tff7b72def Td2a8ffclient_disconnectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient disconnectedTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffserver_buffer_readyTb4b4b4(Te6edf3ready_bytesTb4b4b4: Tffa657intTb4b4b4)Tb4b4b4:
T8b949e"""
Callback from buffer when buffer has data available
:param ready_bytes: The number of bytes ready to read
"""
Tff7b72global Te6edf3latest_buffer
Te6edf3data Tff7b72= Te6edf3latest_bufferTff7b72.Td2a8ffreadTb4b4b4(Te6edf3ready_bytesTb4b4b4)
Te6edf3data Tff7b72= Te6edf3dataTff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffReceived data over the buffer: Ta5d6ff" Tff7b72+ Te6edf3dataTb4b4b4)
Te6edf3reply_message Tff7b72= Ta5d6ff"Ta5d6ffI received Tffea00\"Ta5d6ff"Tff7b72+Te6edf3dataTff7b72+Ta5d6ff"Tffea00\"Ta5d6ff over the bufferTa5d6ff"
Te6edf3reply_message Tff7b72= Te6edf3reply_messageTff7b72.Td2a8ffencodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Te6edf3latest_bufferTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3reply_messageTb4b4b4)
Te6edf3latest_bufferTff7b72.Td2a8ffflushTb4b4b4(Tb4b4b4)
T8b949e##########################################################
T8b949e#### Client Part #########################################
T8b949e##########################################################
T8b949e# A reference to the server link
Te6edf3server_link Tff7b72= Tff7b72None
T8b949e# A reference to the buffer object, needed to share the
T8b949e# object from the link connected callback to the client
T8b949e# loop.
Te6edf3buffer Tff7b72= Tff7b72None
T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a client
Tff7b72def Td2a8ffclientTb4b4b4(Te6edf3destination_hexhashTb4b4b4, Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We need a binary representation of the destination
T8b949e# hash that was entered on the command line
Tff7b72tryTb4b4b4:
Te6edf3dest_len Tff7b72= Tb4b4b4(Te6edf3RNSTff7b72.Td2a8ffReticulumTff7b72.Td2a8ffTRUNCATED_HASHLENGTHTff7b72/Tff7b72/T79c0ff8Tb4b4b4)Tff7b72*T79c0ff2
Tff7b72if Tffa657lenTb4b4b4(Te6edf3destination_hexhashTb4b4b4) Tff7b72!= Te6edf3dest_lenTb4b4b4:
Tff7b72raise Tf85149ValueErrorTb4b4b4(
Ta5d6ff"Ta5d6ffDestination length is invalid, must be Tffd700{hex}Ta5d6ff hexadecimal characters (Tffd700{byte}Ta5d6ff bytes).Ta5d6ff"Tff7b72.Td2a8ffformatTb4b4b4(Tffa657hexTff7b72=Te6edf3dest_lenTb4b4b4, Te6edf3byteTff7b72=Te6edf3dest_lenTff7b72/Tff7b72/T79c0ff2Tb4b4b4)
Tb4b4b4)
Te6edf3destination_hash Tff7b72= Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Te6edf3destination_hexhashTb4b4b4)
Tff7b72exceptTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffInvalid destination entered. Check your input!Tffea00\nTa5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Check if we know a path to the destination
Tff7b72if Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffDestination is not yet known. Requesting path and waiting for announce to arrive...Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffrequest_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)
Tff7b72while Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)
T8b949e# Recall the server identity
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTff7b72.Td2a8ffrecallTb4b4b4(Te6edf3destination_hashTb4b4b4)
T8b949e# Inform the user that we'll begin connecting
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffEstablishing link with server...Ta5d6ff"Tb4b4b4)
T8b949e# When the server identity is known, we set
T8b949e# up a destination
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffOUTTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffbufferexampleTa5d6ff"
Tb4b4b4)
T8b949e# And create a link
Te6edf3link Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTb4b4b4(Te6edf3server_destinationTb4b4b4)
T8b949e# We'll also set up functions to inform the
T8b949e# user when the link is established or closed
Te6edf3linkTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3link_establishedTb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3link_closedTb4b4b4)
T8b949e# Everything is set up, so let's enter a loop
T8b949e# for the user to interact with the example
Te6edf3client_loopTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ffclient_loopTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3server_link
T8b949e# Wait for the link to become active
Tff7b72while Tff7b72not Te6edf3server_linkTb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)
Te6edf3should_quit Tff7b72= Tff7b72False
Tff7b72while Tff7b72not Te6edf3should_quitTb4b4b4:
Tff7b72tryTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff> Ta5d6ff"Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4)
Te6edf3text Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
T8b949e# Check if we should quit the example
Tff7b72if Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffquitTa5d6ff" Tff7b72or Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffqTa5d6ff" Tff7b72or Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffexitTa5d6ff"Tb4b4b4:
Te6edf3should_quit Tff7b72= Tff7b72True
Te6edf3server_linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
Tff7b72elseTb4b4b4:
T8b949e# Otherwise, encode the text and write it to the buffer.
Te6edf3text Tff7b72= Te6edf3textTff7b72.Td2a8ffencodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Te6edf3bufferTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3textTb4b4b4)
T8b949e# Flush the buffer to force the data to be sent.
Te6edf3bufferTff7b72.Td2a8ffflushTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffError while sending data over the link buffer: Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4)
Te6edf3should_quit Tff7b72= Tff7b72True
Te6edf3server_linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
T8b949e# This function is called when a link
T8b949e# has been established with the server
Tff7b72def Td2a8fflink_establishedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
T8b949e# We store a reference to the link
T8b949e# instance for later use
Tff7b72global Te6edf3server_linkTb4b4b4, Te6edf3buffer
Te6edf3server_link Tff7b72= Te6edf3link
T8b949e# Create buffer, see server_client_connected() for
T8b949e# more detail about setting up the buffer.
Te6edf3channel Tff7b72= Te6edf3linkTff7b72.Td2a8ffget_channelTb4b4b4(Tb4b4b4)
Te6edf3buffer Tff7b72= Te6edf3RNSTff7b72.Td2a8ffBufferTff7b72.Td2a8ffcreate_bidirectional_bufferTb4b4b4(T79c0ff0Tb4b4b4, T79c0ff0Tb4b4b4, Te6edf3channelTb4b4b4, Te6edf3client_buffer_readyTb4b4b4)
T8b949e# Inform the user that the server is
T8b949e# connected
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink established with server, enter some text to send, or Tffea00\"Ta5d6ffquitTffea00\"Ta5d6ff to quitTa5d6ff"Tb4b4b4)
T8b949e# When a link is closed, we'll inform the
T8b949e# user, and exit the program
Tff7b72def Td2a8fflink_closedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffTIMEOUTTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link timed out, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elif Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffDESTINATION_CLOSEDTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link was closed by the server, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink closed, exiting nowTa5d6ff"Tb4b4b4)
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff1.5Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
T8b949e# When the buffer has new data, read it and write it to the terminal.
Tff7b72def Td2a8ffclient_buffer_readyTb4b4b4(Te6edf3ready_bytesTb4b4b4: Tffa657intTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3buffer
Te6edf3data Tff7b72= Te6edf3bufferTff7b72.Td2a8ffreadTb4b4b4(Te6edf3ready_bytesTb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffReceived data over the link buffer: Ta5d6ff" Tff7b72+ Te6edf3dataTff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff> Ta5d6ff"Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffstdoutTff7b72.Td2a8ffflushTb4b4b4(Tb4b4b4)
T8b949e##########################################################
T8b949e#### Program Startup #####################################
T8b949e##########################################################
T8b949e# This part of the program runs at startup,
T8b949e# and parses input of from the user, and then
T8b949e# starts up the desired program mode.
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3parser Tff7b72= Te6edf3argparseTff7b72.Td2a8ffArgumentParserTb4b4b4(Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffSimple buffer exampleTa5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff-sTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--serverTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstore_trueTa5d6ff"Tb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffwait for incoming link requests from clientsTa5d6ff"
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff--configTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffpath to alternative Reticulum config directoryTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ffdestinationTa5d6ff"Tb4b4b4,
Te6edf3nargsTff7b72=Ta5d6ff"Ta5d6ff?Ta5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffhexadecimal hash of the server destinationTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3args Tff7b72= Te6edf3parserTff7b72.Td2a8ffparse_argsTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3argsTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3configarg Tff7b72= Te6edf3argsTff7b72.Td2a8ffconfig
Tff7b72elseTb4b4b4:
Te6edf3configarg Tff7b72= Tff7b72None
Tff7b72if Te6edf3argsTff7b72.Td2a8ffserverTb4b4b4:
Te6edf3serverTb4b4b4(Te6edf3configargTb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72if Tb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestination Tff7b72== Tff7b72NoneTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffprint_helpTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3clientTb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestinationTb4b4b4, Te6edf3configargTb4b4b4)
Tff7b72except Tf85149KeyboardInterruptTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
This example can also be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Buffer.py.
Filetransfer
The Filetransfer example implements a basic file-server program that allow clients to connect and download files. The program uses the Resource interface to efficiently pass files of any size over a Reticulum Link.
T282828
T8b949e##########################################################
T8b949e# This RNS example demonstrates a simple filetransfer #
T8b949e# server and client program. The server will serve a #
T8b949e# directory of files, and the clients can list and #
T8b949e# download files from the server. #
T8b949e# #
T8b949e# Please note that using RNS Resources for large file #
T8b949e# transfers is not recommended, since compression, #
T8b949e# encryption and hashmap sequencing can take a long time #
T8b949e# on systems with slow CPUs, which will probably result #
T8b949e# in the client timing out before the resource sender #
T8b949e# can complete preparing the resource. #
T8b949e# #
T8b949e# If you need to transfer large files, use the Bundle #
T8b949e# class instead, which will automatically slice the data #
T8b949e# into chunks suitable for packing as a Resource. #
T8b949e##########################################################
Te6edf3import Te6edf3os
Te6edf3import Te6edf3sys
Te6edf3import Te6edf3time
Te6edf3import Te6edf3threading
Te6edf3import Te6edf3argparse
Te6edf3import Te6edf3RNS
Te6edf3import Te6edf3RNSTff7b72.Td2a8ffvendorTff7b72.Td2a8ffumsgpack Tff7b72as Te6edf3umsgpack
T8b949e# Let's define an app name. We'll use this for all
T8b949e# destinations we create. Since this echo example
T8b949e# is part of a range of example utilities, we'll put
T8b949e# them all within the app namespace "example_utilities"
Te6edf3APP_NAME Tff7b72= Ta5d6ff"Ta5d6ffexample_utilitiesTa5d6ff"
T8b949e# We'll also define a default timeout, in seconds
Te6edf3APP_TIMEOUT Tff7b72= T79c0ff45.0
T8b949e##########################################################
T8b949e#### Server Part #########################################
T8b949e##########################################################
Te6edf3serve_path Tff7b72= Te6edf3None
T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a server
Te6edf3def Te6edf3serverTb4b4b4(Te6edf3configpathTb4b4b4, Te6edf3pathTb4b4b4)Tb4b4b4:
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Randomly create a new identity for our file server
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTb4b4b4(Tb4b4b4)
Te6edf3global Te6edf3serve_path
Te6edf3serve_path Tff7b72= Te6edf3path
T8b949e# We create a destination that clients can connect to. We
T8b949e# want clients to create links to this destination, so we
T8b949e# need to create a "single" destination type.
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffINTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6fffiletransferTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffserverTa5d6ff"
Tb4b4b4)
T8b949e# We configure a function that will get called every time
T8b949e# a new client creates a link to this destination.
Te6edf3server_destinationTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3client_connectedTb4b4b4)
T8b949e# Everything's ready!
T8b949e# Let's Wait for client requests or user input
Te6edf3announceLoopTb4b4b4(Te6edf3server_destinationTb4b4b4)
Te6edf3def Te6edf3announceLoopTb4b4b4(Te6edf3destinationTb4b4b4)Tb4b4b4:
T8b949e# Let the user know that everything is ready
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffFile server Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff runningTa5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffHit enter to manually send an announce (Ctrl-C to quit)Ta5d6ff"Tb4b4b4)
T8b949e# We enter a loop that runs until the users exits.
T8b949e# If the user hits enter, we will announce our server
T8b949e# destination on the network, which will let clients
T8b949e# know how to create messages directed towards it.
Tff7b72while Te6edf3TrueTb4b4b4:
Te6edf3entered Tff7b72= Te6edf3inputTb4b4b4(Tb4b4b4)
Te6edf3destinationTff7b72.Td2a8ffannounceTb4b4b4(Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffSent announce from Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tb4b4b4)
T8b949e# Here's a convenience function for listing all files
T8b949e# in our served directory
Te6edf3def Te6edf3list_filesTb4b4b4(Tb4b4b4)Tb4b4b4:
T8b949e# We add all entries from the directory that are
T8b949e# actual files, and does not start with "."
Te6edf3global Te6edf3serve_path
Tff7b72return Tb4b4b4[Te6edf3file Tff7b72for Te6edf3file Tff7b72in Te6edf3osTff7b72.Td2a8fflistdirTb4b4b4(Te6edf3serve_pathTb4b4b4) Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3serve_pathTb4b4b4, Te6edf3fileTb4b4b4)Tb4b4b4) Tff7b72and Te6edf3fileTb4b4b4[Tb4b4b4:T79c0ff1Tb4b4b4] Tff7b72!= Ta5d6ff"Ta5d6ff.Ta5d6ff"Tb4b4b4]
T8b949e# When a client establishes a link to our server
T8b949e# destination, this function will be called with
T8b949e# a reference to the link. We then send the client
T8b949e# a list of files hosted on the server.
Te6edf3def Te6edf3client_connectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
T8b949e# Check if the served directory still exists
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisdirTb4b4b4(Te6edf3serve_pathTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient connected, sending file list...Ta5d6ff"Tb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3client_disconnectedTb4b4b4)
T8b949e# We pack a list of files for sending in a packet
Te6edf3data Tff7b72= Te6edf3umsgpackTff7b72.Td2a8ffpackbTb4b4b4(Te6edf3list_filesTb4b4b4(Tb4b4b4)Tb4b4b4)
T8b949e# Check the size of the packed data
Tff7b72if Te6edf3lenTb4b4b4(Te6edf3dataTb4b4b4) Tff7b72<Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffMDUTb4b4b4:
T8b949e# If it fits in one packet, we will just
T8b949e# send it as a single packet over the link.
Te6edf3list_packet Tff7b72= Te6edf3RNSTff7b72.Td2a8ffPacketTb4b4b4(Te6edf3linkTb4b4b4, Te6edf3dataTb4b4b4)
Te6edf3list_receipt Tff7b72= Te6edf3list_packetTff7b72.Td2a8ffsendTb4b4b4(Tb4b4b4)
Te6edf3list_receiptTff7b72.Td2a8ffset_timeoutTb4b4b4(Te6edf3APP_TIMEOUTTb4b4b4)
Te6edf3list_receiptTff7b72.Td2a8ffset_delivery_callbackTb4b4b4(Te6edf3list_deliveredTb4b4b4)
Te6edf3list_receiptTff7b72.Td2a8ffset_timeout_callbackTb4b4b4(Te6edf3list_timeoutTb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffToo many files in served directory!Ta5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffYou should implement a function to split the filelist over multiple packets.Ta5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffHint: The client already supports it :)Ta5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4)
T8b949e# After this, we're just going to keep the link
T8b949e# open until the client requests a file. We'll
T8b949e# configure a function that get's called when
T8b949e# the client sends a packet with a file request.
Te6edf3linkTff7b72.Td2a8ffset_packet_callbackTb4b4b4(Te6edf3client_requestTb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient connected, but served path no longer exists!Ta5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4)
Te6edf3linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
Te6edf3def Te6edf3client_disconnectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient disconnectedTa5d6ff"Tb4b4b4)
Te6edf3def Te6edf3client_requestTb4b4b4(Te6edf3messageTb4b4b4, Te6edf3packetTb4b4b4)Tb4b4b4:
Te6edf3global Te6edf3serve_path
Te6edf3tryTb4b4b4:
Te6edf3filename Tff7b72= Te6edf3messageTff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Te6edf3except Te6edf3Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3filename Tff7b72= Te6edf3None
Tff7b72if Te6edf3filename Tff7b72in Te6edf3list_filesTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3tryTb4b4b4:
T8b949e# If we have the requested file, we'll
T8b949e# read it and pack it as a resource
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient requested Tffea00\"Ta5d6ff"Tff7b72+Te6edf3filenameTff7b72+Ta5d6ff"Tffea00\"Ta5d6ff"Tb4b4b4)
Te6edf3file Tff7b72= Te6edf3openTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3serve_pathTb4b4b4, Te6edf3filenameTb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffrbTa5d6ff"Tb4b4b4)
Te6edf3file_resource Tff7b72= Te6edf3RNSTff7b72.Td2a8ffResourceTb4b4b4(
Te6edf3fileTb4b4b4,
Te6edf3packetTff7b72.Td2a8fflinkTb4b4b4,
Te6edf3callbackTff7b72=Te6edf3resource_sending_concluded
Tb4b4b4)
Te6edf3file_resourceTff7b72.Td2a8fffilename Tff7b72= Te6edf3filename
Te6edf3except Te6edf3Exception Tff7b72as Te6edf3eTb4b4b4:
T8b949e# If somethign went wrong, we close
T8b949e# the link
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffError while reading file Tffea00\"Ta5d6ff"Tff7b72+Te6edf3filenameTff7b72+Ta5d6ff"Tffea00\"Ta5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4)
Te6edf3packetTff7b72.Td2a8fflinkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
Te6edf3raise Te6edf3e
Tff7b72elseTb4b4b4:
T8b949e# If we don't have it, we close the link
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient requested an unknown fileTa5d6ff"Tb4b4b4)
Te6edf3packetTff7b72.Td2a8fflinkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
T8b949e# This function is called on the server when a
T8b949e# resource transfer concludes.
Te6edf3def Te6edf3resource_sending_concludedTb4b4b4(Te6edf3resourceTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3hasattrTb4b4b4(Te6edf3resourceTb4b4b4, Ta5d6ff"Ta5d6fffilenameTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3name Tff7b72= Te6edf3resourceTff7b72.Td2a8fffilename
Tff7b72elseTb4b4b4:
Te6edf3name Tff7b72= Ta5d6ff"Ta5d6ffresourceTa5d6ff"
Tff7b72if Te6edf3resourceTff7b72.Td2a8ffstatus Tff7b72== Te6edf3RNSTff7b72.Td2a8ffResourceTff7b72.Td2a8ffCOMPLETETb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffDone sending Tffea00\"Ta5d6ff"Tff7b72+Te6edf3nameTff7b72+Ta5d6ff"Tffea00\"Ta5d6ff to clientTa5d6ff"Tb4b4b4)
Tff7b72elif Te6edf3resourceTff7b72.Td2a8ffstatus Tff7b72== Te6edf3RNSTff7b72.Td2a8ffResourceTff7b72.Td2a8ffFAILEDTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffSending Tffea00\"Ta5d6ff"Tff7b72+Te6edf3nameTff7b72+Ta5d6ff"Tffea00\"Ta5d6ff to client failedTa5d6ff"Tb4b4b4)
Te6edf3def Te6edf3list_deliveredTb4b4b4(Te6edf3receiptTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe file list was received by the clientTa5d6ff"Tb4b4b4)
Te6edf3def Te6edf3list_timeoutTb4b4b4(Te6edf3receiptTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffSending list to client timed out, closing this linkTa5d6ff"Tb4b4b4)
Te6edf3link Tff7b72= Te6edf3receiptTff7b72.Td2a8ffdestination
Te6edf3linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
T8b949e##########################################################
T8b949e#### Client Part #########################################
T8b949e##########################################################
T8b949e# We store a global list of files available on the server
Te6edf3server_files Tff7b72= Tb4b4b4[Tb4b4b4]
T8b949e# A reference to the server link
Te6edf3server_link Tff7b72= Te6edf3None
T8b949e# And a reference to the current download
Te6edf3current_download Tff7b72= Te6edf3None
Te6edf3current_filename Tff7b72= Te6edf3None
T8b949e# Variables to store download statistics
Te6edf3download_started Tff7b72= T79c0ff0
Te6edf3download_finished Tff7b72= T79c0ff0
Te6edf3download_time Tff7b72= T79c0ff0
Te6edf3transfer_size Tff7b72= T79c0ff0
Te6edf3file_size Tff7b72= T79c0ff0
T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a client
Te6edf3def Te6edf3clientTb4b4b4(Te6edf3destination_hexhashTb4b4b4, Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We need a binary representation of the destination
T8b949e# hash that was entered on the command line
Te6edf3tryTb4b4b4:
Te6edf3dest_len Tff7b72= Tb4b4b4(Te6edf3RNSTff7b72.Td2a8ffReticulumTff7b72.Td2a8ffTRUNCATED_HASHLENGTHTff7b72/Tff7b72/T79c0ff8Tb4b4b4)Tff7b72*T79c0ff2
Tff7b72if Te6edf3lenTb4b4b4(Te6edf3destination_hexhashTb4b4b4) Tff7b72!= Te6edf3dest_lenTb4b4b4:
Te6edf3raise Te6edf3ValueErrorTb4b4b4(
Ta5d6ff"Ta5d6ffDestination length is invalid, must be {hex} hexadecimal characters ({byte} bytes).Ta5d6ff"Tff7b72.Td2a8ffformatTb4b4b4(Te6edf3hexTff7b72=Te6edf3dest_lenTb4b4b4, Te6edf3byteTff7b72=Te6edf3dest_lenTff7b72/Tff7b72/T79c0ff2Tb4b4b4)
Tb4b4b4)
Te6edf3destination_hash Tff7b72= Te6edf3bytesTff7b72.Td2a8fffromhexTb4b4b4(Te6edf3destination_hexhashTb4b4b4)
Te6edf3exceptTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffInvalid destination entered. Check your input!Tffea00\nTa5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Check if we know a path to the destination
Tff7b72if Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffDestination is not yet known. Requesting path and waiting for announce to arrive...Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffrequest_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)
Tff7b72while Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)
T8b949e# Recall the server identity
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTff7b72.Td2a8ffrecallTb4b4b4(Te6edf3destination_hashTb4b4b4)
T8b949e# Inform the user that we'll begin connecting
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffEstablishing link with server...Ta5d6ff"Tb4b4b4)
T8b949e# When the server identity is known, we set
T8b949e# up a destination
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffOUTTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6fffiletransferTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffserverTa5d6ff"
Tb4b4b4)
T8b949e# We also want to automatically prove incoming packets
Te6edf3server_destinationTff7b72.Td2a8ffset_proof_strategyTb4b4b4(Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffPROVE_ALLTb4b4b4)
T8b949e# And create a link
Te6edf3link Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTb4b4b4(Te6edf3server_destinationTb4b4b4)
T8b949e# We expect any normal data packets on the link
T8b949e# to contain a list of served files, so we set
T8b949e# a callback accordingly
Te6edf3linkTff7b72.Td2a8ffset_packet_callbackTb4b4b4(Te6edf3filelist_receivedTb4b4b4)
T8b949e# We'll also set up functions to inform the
T8b949e# user when the link is established or closed
Te6edf3linkTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3link_establishedTb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3link_closedTb4b4b4)
T8b949e# And set the link to automatically begin
T8b949e# downloading advertised resources
Te6edf3linkTff7b72.Td2a8ffset_resource_strategyTb4b4b4(Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffACCEPT_ALLTb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_resource_started_callbackTb4b4b4(Te6edf3download_beganTb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_resource_concluded_callbackTb4b4b4(Te6edf3download_concludedTb4b4b4)
Te6edf3menuTb4b4b4(Tb4b4b4)
T8b949e# Requests the specified file from the server
Te6edf3def Te6edf3downloadTb4b4b4(Te6edf3filenameTb4b4b4)Tb4b4b4:
Te6edf3global Te6edf3server_linkTb4b4b4, Te6edf3menu_modeTb4b4b4, Te6edf3current_filenameTb4b4b4, Te6edf3transfer_sizeTb4b4b4, Te6edf3download_started
Te6edf3current_filename Tff7b72= Te6edf3filename
Te6edf3download_started Tff7b72= T79c0ff0
Te6edf3transfer_size Tff7b72= T79c0ff0
T8b949e# We just create a packet containing the
T8b949e# requested filename, and send it down the
T8b949e# link. We also specify we don't need a
T8b949e# packet receipt.
Te6edf3request_packet Tff7b72= Te6edf3RNSTff7b72.Td2a8ffPacketTb4b4b4(Te6edf3server_linkTb4b4b4, Te6edf3filenameTff7b72.Td2a8ffencodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)Tb4b4b4, Te6edf3create_receiptTff7b72=Te6edf3FalseTb4b4b4)
Te6edf3request_packetTff7b72.Td2a8ffsendTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Tb4b4b4(Ta5d6ff"Ta5d6ffRequested Tffea00\"Ta5d6ff"Tff7b72+Te6edf3filenameTff7b72+Ta5d6ff"Tffea00\"Ta5d6ff from server, waiting for download to begin...Ta5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3menu_mode Tff7b72= Ta5d6ff"Ta5d6ffdownload_startedTa5d6ff"
T8b949e# This function runs a simple menu for the user
T8b949e# to select which files to download, or quit
Te6edf3menu_mode Tff7b72= Te6edf3None
Te6edf3def Te6edf3menuTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3global Te6edf3server_filesTb4b4b4, Te6edf3server_link
T8b949e# Wait until we have a filelist
Tff7b72while Te6edf3lenTb4b4b4(Te6edf3server_filesTb4b4b4) Tff7b72== T79c0ff0Tb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffReady!Ta5d6ff"Tb4b4b4)
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.5Tb4b4b4)
Te6edf3global Te6edf3menu_mode
Te6edf3menu_mode Tff7b72= Ta5d6ff"Ta5d6ffmainTa5d6ff"
Te6edf3should_quit Tff7b72= Te6edf3False
Tff7b72while Tb4b4b4(Tff7b72not Te6edf3should_quitTb4b4b4)Tb4b4b4:
Te6edf3print_menuTb4b4b4(Tb4b4b4)
Tff7b72while Tff7b72not Te6edf3menu_mode Tff7b72== Ta5d6ff"Ta5d6ffmainTa5d6ff"Tb4b4b4:
T8b949e# Wait
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.25Tb4b4b4)
Te6edf3user_input Tff7b72= Te6edf3inputTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3user_input Tff7b72== Ta5d6ff"Ta5d6ffqTa5d6ff" Tff7b72or Te6edf3user_input Tff7b72== Ta5d6ff"Ta5d6ffquitTa5d6ff" Tff7b72or Te6edf3user_input Tff7b72== Ta5d6ff"Ta5d6ffexitTa5d6ff"Tb4b4b4:
Te6edf3should_quit Tff7b72= Te6edf3True
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72if Te6edf3user_input Tff7b72in Te6edf3server_filesTb4b4b4:
Te6edf3downloadTb4b4b4(Te6edf3user_inputTb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3tryTb4b4b4:
Tff7b72if T79c0ff0 Tff7b72<Tff7b72= Tffa657intTb4b4b4(Te6edf3user_inputTb4b4b4) Tff7b72< Te6edf3lenTb4b4b4(Te6edf3server_filesTb4b4b4)Tb4b4b4:
Te6edf3downloadTb4b4b4(Te6edf3server_filesTb4b4b4[Tffa657intTb4b4b4(Te6edf3user_inputTb4b4b4)Tb4b4b4]Tb4b4b4)
Te6edf3exceptTb4b4b4:
Tff7b72pass
Tff7b72if Te6edf3should_quitTb4b4b4:
Te6edf3server_linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
T8b949e# Prints out menus or screens for the
T8b949e# various states of the client program.
T8b949e# It's simple and quite uninteresting.
T8b949e# I won't go into detail here. Just
T8b949e# strings basically.
Te6edf3def Te6edf3print_menuTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3global Te6edf3menu_modeTb4b4b4, Te6edf3download_timeTb4b4b4, Te6edf3download_startedTb4b4b4, Te6edf3download_finishedTb4b4b4, Te6edf3transfer_sizeTb4b4b4, Te6edf3file_size
Tff7b72if Te6edf3menu_mode Tff7b72== Ta5d6ff"Ta5d6ffmainTa5d6ff"Tb4b4b4:
Te6edf3clear_screenTb4b4b4(Tb4b4b4)
Te6edf3print_filelistTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffSelect a file to download by entering name or number, or q to quitTa5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Tb4b4b4(Ta5d6ff"Ta5d6ff> Ta5d6ff"Tb4b4b4)Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff'Ta5d6ff Ta5d6ff'Tb4b4b4)
Tff7b72elif Te6edf3menu_mode Tff7b72== Ta5d6ff"Ta5d6ffdownload_startedTa5d6ff"Tb4b4b4:
Te6edf3download_began Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
Tff7b72while Te6edf3menu_mode Tff7b72== Ta5d6ff"Ta5d6ffdownload_startedTa5d6ff"Tb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)
Tff7b72if Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4) Tff7b72> Te6edf3download_beganTff7b72+Te6edf3APP_TIMEOUTTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffThe download timed outTa5d6ff"Tb4b4b4)
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff1Tb4b4b4)
Te6edf3server_linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3menu_mode Tff7b72== Ta5d6ff"Ta5d6ffdownloadingTa5d6ff"Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffDownload startedTa5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72while Te6edf3menu_mode Tff7b72== Ta5d6ff"Ta5d6ffdownloadingTa5d6ff"Tb4b4b4:
Te6edf3global Te6edf3current_download
Te6edf3percent Tff7b72= Tffa657roundTb4b4b4(Te6edf3current_downloadTff7b72.Td2a8ffget_progressTb4b4b4(Tb4b4b4) Tff7b72* T79c0ff100.0Tb4b4b4, T79c0ff1Tb4b4b4)
Tffa657printTb4b4b4(Tb4b4b4(Ta5d6ff"Tffea00\rTa5d6ffProgress: Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3percentTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff Ta5d6ff%Ta5d6ff Ta5d6ff"Tb4b4b4)Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff'Ta5d6ff Ta5d6ff'Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffstdoutTff7b72.Td2a8ffflushTb4b4b4(Tb4b4b4)
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)
Tff7b72if Te6edf3menu_mode Tff7b72== Ta5d6ff"Ta5d6ffsave_errorTa5d6ff"Tb4b4b4:
Tffa657printTb4b4b4(Tb4b4b4(Ta5d6ff"Tffea00\rTa5d6ffProgress: 100.0 Ta5d6ff%Ta5d6ff"Tb4b4b4)Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff'Ta5d6ff Ta5d6ff'Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffstdoutTff7b72.Td2a8ffflushTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffCould not write downloaded file to diskTa5d6ff"Tb4b4b4)
Te6edf3current_downloadTff7b72.Td2a8ffstatus Tff7b72= Te6edf3RNSTff7b72.Td2a8ffResourceTff7b72.Td2a8ffFAILED
Te6edf3menu_mode Tff7b72= Ta5d6ff"Ta5d6ffdownload_concludedTa5d6ff"
Tff7b72if Te6edf3menu_mode Tff7b72== Ta5d6ff"Ta5d6ffdownload_concludedTa5d6ff"Tb4b4b4:
Tff7b72if Te6edf3current_downloadTff7b72.Td2a8ffstatus Tff7b72== Te6edf3RNSTff7b72.Td2a8ffResourceTff7b72.Td2a8ffCOMPLETETb4b4b4:
Tffa657printTb4b4b4(Tb4b4b4(Ta5d6ff"Tffea00\rTa5d6ffProgress: 100.0 Ta5d6ff%Ta5d6ff"Tb4b4b4)Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff'Ta5d6ff Ta5d6ff'Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffstdoutTff7b72.Td2a8ffflushTb4b4b4(Tb4b4b4)
T8b949e# Print statistics
Te6edf3hoursTb4b4b4, Te6edf3rem Tff7b72= Te6edf3divmodTb4b4b4(Te6edf3download_timeTb4b4b4, T79c0ff3600Tb4b4b4)
Te6edf3minutesTb4b4b4, Te6edf3seconds Tff7b72= Te6edf3divmodTb4b4b4(Te6edf3remTb4b4b4, T79c0ff60Tb4b4b4)
Te6edf3timestring Tff7b72= Ta5d6ff"Ta5d6ff{:0>2}:{:0>2}:{:05.2f}Ta5d6ff"Tff7b72.Td2a8ffformatTb4b4b4(Tffa657intTb4b4b4(Te6edf3hoursTb4b4b4)Tb4b4b4,Tffa657intTb4b4b4(Te6edf3minutesTb4b4b4)Tb4b4b4,Te6edf3secondsTb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff--- Statistics -----Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Tffea00\tTa5d6ffTime taken : Ta5d6ff"Tff7b72+Te6edf3timestringTb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Tffea00\tTa5d6ffFile size : Ta5d6ff"Tff7b72+Te6edf3size_strTb4b4b4(Te6edf3file_sizeTb4b4b4)Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Tffea00\tTa5d6ffData transferred : Ta5d6ff"Tff7b72+Te6edf3size_strTb4b4b4(Te6edf3transfer_sizeTb4b4b4)Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Tffea00\tTa5d6ffEffective rate : Ta5d6ff"Tff7b72+Te6edf3size_strTb4b4b4(Te6edf3file_sizeTff7b72/Te6edf3download_timeTb4b4b4, Te6edf3suffixTff7b72=Ta5d6ff'Ta5d6ffbTa5d6ff'Tb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff/sTa5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Tffea00\tTa5d6ffTransfer rate : Ta5d6ff"Tff7b72+Te6edf3size_strTb4b4b4(Te6edf3transfer_sizeTff7b72/Te6edf3download_timeTb4b4b4, Te6edf3suffixTff7b72=Ta5d6ff'Ta5d6ffbTa5d6ff'Tb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff/sTa5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffThe download completed! Press enter to return to the menu.Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3inputTb4b4b4(Tb4b4b4)
Tff7b72elseTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffThe download failed! Press enter to return to the menu.Ta5d6ff"Tb4b4b4)
Te6edf3inputTb4b4b4(Tb4b4b4)
Te6edf3current_download Tff7b72= Te6edf3None
Te6edf3menu_mode Tff7b72= Ta5d6ff"Ta5d6ffmainTa5d6ff"
Te6edf3print_menuTb4b4b4(Tb4b4b4)
T8b949e# This function prints out a list of files
T8b949e# on the connected server.
Te6edf3def Te6edf3print_filelistTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3global Te6edf3server_files
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffFiles on server:Ta5d6ff"Tb4b4b4)
Tff7b72for Te6edf3indexTb4b4b4,Te6edf3file Tff7b72in Te6edf3enumerateTb4b4b4(Te6edf3server_filesTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Tffea00\tTa5d6ff(Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3indexTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff)Tffea00\tTa5d6ff"Tff7b72+Te6edf3fileTb4b4b4)
Te6edf3def Te6edf3filelist_receivedTb4b4b4(Te6edf3filelist_dataTb4b4b4, Te6edf3packetTb4b4b4)Tb4b4b4:
Te6edf3global Te6edf3server_filesTb4b4b4, Te6edf3menu_mode
Te6edf3tryTb4b4b4:
T8b949e# Unpack the list and extend our
T8b949e# local list of available files
Te6edf3filelist Tff7b72= Te6edf3umsgpackTff7b72.Td2a8ffunpackbTb4b4b4(Te6edf3filelist_dataTb4b4b4)
Tff7b72for Te6edf3file Tff7b72in Te6edf3filelistTb4b4b4:
Tff7b72if Tff7b72not Te6edf3file Tff7b72in Te6edf3server_filesTb4b4b4:
Te6edf3server_filesTff7b72.Td2a8ffappendTb4b4b4(Te6edf3fileTb4b4b4)
T8b949e# If the menu is already visible,
T8b949e# we'll update it with what was
T8b949e# just received
Tff7b72if Te6edf3menu_mode Tff7b72== Ta5d6ff"Ta5d6ffmainTa5d6ff"Tb4b4b4:
Te6edf3print_menuTb4b4b4(Tb4b4b4)
Te6edf3exceptTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffInvalid file list data received, closing linkTa5d6ff"Tb4b4b4)
Te6edf3packetTff7b72.Td2a8fflinkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
T8b949e# This function is called when a link
T8b949e# has been established with the server
Te6edf3def Te6edf3link_establishedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
T8b949e# We store a reference to the link
T8b949e# instance for later use
Te6edf3global Te6edf3server_link
Te6edf3server_link Tff7b72= Te6edf3link
T8b949e# Inform the user that the server is
T8b949e# connected
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink established with serverTa5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffWaiting for filelist...Ta5d6ff"Tb4b4b4)
T8b949e# And set up a small job to check for
T8b949e# a potential timeout in receiving the
T8b949e# file list
Te6edf3thread Tff7b72= Te6edf3threadingTff7b72.Td2a8ffThreadTb4b4b4(Te6edf3targetTff7b72=Te6edf3filelist_timeout_jobTb4b4b4, Te6edf3daemonTff7b72=Te6edf3TrueTb4b4b4)
Te6edf3threadTff7b72.Td2a8ffstartTb4b4b4(Tb4b4b4)
T8b949e# This job just sleeps for the specified
T8b949e# time, and then checks if the file list
T8b949e# was received. If not, the program will
T8b949e# exit.
Te6edf3def Te6edf3filelist_timeout_jobTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(Te6edf3APP_TIMEOUTTb4b4b4)
Te6edf3global Te6edf3server_files
Tff7b72if Te6edf3lenTb4b4b4(Te6edf3server_filesTb4b4b4) Tff7b72== T79c0ff0Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffTimed out waiting for filelist, exitingTa5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
T8b949e# When a link is closed, we'll inform the
T8b949e# user, and exit the program
Te6edf3def Te6edf3link_closedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffTIMEOUTTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link timed out, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elif Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffDESTINATION_CLOSEDTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link was closed by the server, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink closed, exiting nowTa5d6ff"Tb4b4b4)
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff1.5Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
T8b949e# When RNS detects that the download has
T8b949e# started, we'll update our menu state
T8b949e# so the user can be shown a progress of
T8b949e# the download.
Te6edf3def Te6edf3download_beganTb4b4b4(Te6edf3resourceTb4b4b4)Tb4b4b4:
Te6edf3global Te6edf3menu_modeTb4b4b4, Te6edf3current_downloadTb4b4b4, Te6edf3download_startedTb4b4b4, Te6edf3transfer_sizeTb4b4b4, Te6edf3file_size
Te6edf3current_download Tff7b72= Te6edf3resource
Tff7b72if Te6edf3download_started Tff7b72== T79c0ff0Tb4b4b4:
Te6edf3download_started Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
Te6edf3transfer_size Tff7b72+= Te6edf3resourceTff7b72.Td2a8ffsize
Te6edf3file_size Tff7b72= Te6edf3resourceTff7b72.Td2a8fftotal_size
Te6edf3menu_mode Tff7b72= Ta5d6ff"Ta5d6ffdownloadingTa5d6ff"
T8b949e# When the download concludes, successfully
T8b949e# or not, we'll update our menu state and
T8b949e# inform the user about how it all went.
Te6edf3def Te6edf3download_concludedTb4b4b4(Te6edf3resourceTb4b4b4)Tb4b4b4:
Te6edf3global Te6edf3menu_modeTb4b4b4, Te6edf3current_filenameTb4b4b4, Te6edf3download_startedTb4b4b4, Te6edf3download_finishedTb4b4b4, Te6edf3download_time
Te6edf3download_finished Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
Te6edf3download_time Tff7b72= Te6edf3download_finished Tff7b72- Te6edf3download_started
Te6edf3saved_filename Tff7b72= Te6edf3current_filename
Tff7b72if Te6edf3resourceTff7b72.Td2a8ffstatus Tff7b72== Te6edf3RNSTff7b72.Td2a8ffResourceTff7b72.Td2a8ffCOMPLETETb4b4b4:
Te6edf3counter Tff7b72= T79c0ff0
Tff7b72while Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3saved_filenameTb4b4b4)Tb4b4b4:
Te6edf3counter Tff7b72+= T79c0ff1
Te6edf3saved_filename Tff7b72= Te6edf3current_filenameTff7b72+Ta5d6ff"Ta5d6ff.Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3counterTb4b4b4)
Te6edf3tryTb4b4b4:
Te6edf3file Tff7b72= Te6edf3openTb4b4b4(Te6edf3saved_filenameTb4b4b4, Ta5d6ff"Ta5d6ffwbTa5d6ff"Tb4b4b4)
Te6edf3fileTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3resourceTff7b72.Td2a8ffdataTff7b72.Td2a8ffreadTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3fileTff7b72.Td2a8ffcloseTb4b4b4(Tb4b4b4)
Te6edf3menu_mode Tff7b72= Ta5d6ff"Ta5d6ffdownload_concludedTa5d6ff"
Te6edf3exceptTb4b4b4:
Te6edf3menu_mode Tff7b72= Ta5d6ff"Ta5d6ffsave_errorTa5d6ff"
Tff7b72elseTb4b4b4:
Te6edf3menu_mode Tff7b72= Ta5d6ff"Ta5d6ffdownload_concludedTa5d6ff"
T8b949e# A convenience function for printing a human-
T8b949e# readable file size
Te6edf3def Te6edf3size_strTb4b4b4(Te6edf3numTb4b4b4, Te6edf3suffixTff7b72=Ta5d6ff'Ta5d6ffBTa5d6ff'Tb4b4b4)Tb4b4b4:
Te6edf3units Tff7b72= Tb4b4b4[Ta5d6ff'Ta5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffKiTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffMiTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffGiTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffTiTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffPiTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffEiTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffZiTa5d6ff'Tb4b4b4]
Te6edf3last_unit Tff7b72= Ta5d6ff'Ta5d6ffYiTa5d6ff'
Tff7b72if Te6edf3suffix Tff7b72== Ta5d6ff'Ta5d6ffbTa5d6ff'Tb4b4b4:
Te6edf3num Tff7b72*= T79c0ff8
Te6edf3units Tff7b72= Tb4b4b4[Ta5d6ff'Ta5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffKTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffMTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffGTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffTTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffPTa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffETa5d6ff'Tb4b4b4,Ta5d6ff'Ta5d6ffZTa5d6ff'Tb4b4b4]
Te6edf3last_unit Tff7b72= Ta5d6ff'Ta5d6ffYTa5d6ff'
Tff7b72for Te6edf3unit Tff7b72in Te6edf3unitsTb4b4b4:
Tff7b72if Tffa657absTb4b4b4(Te6edf3numTb4b4b4) Tff7b72< T79c0ff1024.0Tb4b4b4:
Tff7b72return Ta5d6ff"Tffd700%3.2fTa5d6ff Tffd700%sTffd700%sTa5d6ff" Tff7b72% Tb4b4b4(Te6edf3numTb4b4b4, Te6edf3unitTb4b4b4, Te6edf3suffixTb4b4b4)
Te6edf3num Tff7b72/= T79c0ff1024.0
Tff7b72return Ta5d6ff"Tffd700%.2fTa5d6ff Tffd700%sTffd700%sTa5d6ff" Tff7b72% Tb4b4b4(Te6edf3numTb4b4b4, Te6edf3last_unitTb4b4b4, Te6edf3suffixTb4b4b4)
T8b949e# A convenience function for clearing the screen
Te6edf3def Te6edf3clear_screenTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3osTff7b72.Td2a8ffsystemTb4b4b4(Ta5d6ff'Ta5d6ffclsTa5d6ff' Tff7b72if Te6edf3osTff7b72.Td2a8ffnameTff7b72==Ta5d6ff'Ta5d6ffntTa5d6ff' Tff7b72else Ta5d6ff'Ta5d6ffclearTa5d6ff'Tb4b4b4)
T8b949e##########################################################
T8b949e#### Program Startup #####################################
T8b949e##########################################################
T8b949e# This part of the program runs at startup,
T8b949e# and parses input of from the user, and then
T8b949e# starts up the desired program mode.
Tff7b72if Te6edf3__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Te6edf3tryTb4b4b4:
Te6edf3parser Tff7b72= Te6edf3argparseTff7b72.Td2a8ffArgumentParserTb4b4b4(
Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffSimple file transfer server and client utilityTa5d6ff"
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff-sTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--serveTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3metavarTff7b72=Ta5d6ff"Ta5d6ffdirTa5d6ff"Tb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffserve a directory of files to clientsTa5d6ff"
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff--configTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Te6edf3NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffpath to alternative Reticulum config directoryTa5d6ff"Tb4b4b4,
Te6edf3typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ffdestinationTa5d6ff"Tb4b4b4,
Te6edf3nargsTff7b72=Ta5d6ff"Ta5d6ff?Ta5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Te6edf3NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffhexadecimal hash of the server destinationTa5d6ff"Tb4b4b4,
Te6edf3typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3args Tff7b72= Te6edf3parserTff7b72.Td2a8ffparse_argsTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3argsTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3configarg Tff7b72= Te6edf3argsTff7b72.Td2a8ffconfig
Tff7b72elseTb4b4b4:
Te6edf3configarg Tff7b72= Te6edf3None
Tff7b72if Te6edf3argsTff7b72.Td2a8ffserveTb4b4b4:
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisdirTb4b4b4(Te6edf3argsTff7b72.Td2a8ffserveTb4b4b4)Tb4b4b4:
Te6edf3serverTb4b4b4(Te6edf3configargTb4b4b4, Te6edf3argsTff7b72.Td2a8ffserveTb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe specified directory does not existTa5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72if Tb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestination Tff7b72== Te6edf3NoneTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffprint_helpTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3clientTb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestinationTb4b4b4, Te6edf3configargTb4b4b4)
Te6edf3except Te6edf3KeyboardInterruptTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
This example can also be found at https://github.com/markqvist/Reticulum/blob/master/Examples/Filetransfer.py.
Custom Interfaces
The ExampleInterface demonstrates creating custom interfaces for Reticulum. Any number of custom interfaces can be loaded and utilised by Reticulum, and will be fully on-par with natively included interfaces, including all supported interface modes and common configuration options.
T282828
T8b949e# This example illustrates creating a custom interface
T8b949e# definition, that can be loaded and used by Reticulum at
T8b949e# runtime. Any number of custom interfaces can be created
T8b949e# and loaded. To use the interface place it in the folder
T8b949e# ~/.reticulum/interfaces, and add an interface entry to
T8b949e# your Reticulum configuration file similar to this:
T8b949e# [[Example Custom Interface]]
T8b949e# type = ExampleInterface
T8b949e# enabled = no
T8b949e# mode = gateway
T8b949e# port = /dev/ttyUSB0
T8b949e# speed = 115200
T8b949e# databits = 8
T8b949e# parity = none
T8b949e# stopbits = 1
Tff7b72from T7ee787time Tff7b72import Te6edf3sleep
Tff7b72import T7ee787sys
Tff7b72import T7ee787threading
Tff7b72import T7ee787time
T8b949e# This HDLC helper class is used by the interface
T8b949e# to delimit and packetize data over the physical
T8b949e# medium - in this case a serial connection.
Tff7b72class T56d364HDLCTb4b4b4(Tb4b4b4)Tb4b4b4:
T8b949e# This example interface packetizes data using
T8b949e# simplified HDLC framing, similar to PPP
Te6edf3FLAG Tff7b72= T79c0ff0x7E
Te6edf3ESC Tff7b72= T79c0ff0x7D
Te6edf3ESC_MASK Tff7b72= T79c0ff0x20
Tf0883e@staticmethod
Tff7b72def Td2a8ffescapeTb4b4b4(Te6edf3dataTb4b4b4)Tb4b4b4:
Te6edf3data Tff7b72= Te6edf3dataTff7b72.Td2a8ffreplaceTb4b4b4(Tffa657bytesTb4b4b4(Tb4b4b4[Te6edf3HDLCTff7b72.Td2a8ffESCTb4b4b4]Tb4b4b4)Tb4b4b4, Tffa657bytesTb4b4b4(Tb4b4b4[Te6edf3HDLCTff7b72.Td2a8ffESCTb4b4b4, Te6edf3HDLCTff7b72.Td2a8ffESCTff7b72^Te6edf3HDLCTff7b72.Td2a8ffESC_MASKTb4b4b4]Tb4b4b4)Tb4b4b4)
Te6edf3data Tff7b72= Te6edf3dataTff7b72.Td2a8ffreplaceTb4b4b4(Tffa657bytesTb4b4b4(Tb4b4b4[Te6edf3HDLCTff7b72.Td2a8ffFLAGTb4b4b4]Tb4b4b4)Tb4b4b4, Tffa657bytesTb4b4b4(Tb4b4b4[Te6edf3HDLCTff7b72.Td2a8ffESCTb4b4b4, Te6edf3HDLCTff7b72.Td2a8ffFLAGTff7b72^Te6edf3HDLCTff7b72.Td2a8ffESC_MASKTb4b4b4]Tb4b4b4)Tb4b4b4)
Tff7b72return Te6edf3data
T8b949e# Let's define our custom interface class. It must
T8b949e# be a sub-class of the RNS "Interface" class.
Tff7b72class T56d364ExampleInterfaceTb4b4b4(Te6edf3InterfaceTb4b4b4)Tb4b4b4:
T8b949e# All interface classes must define a default
T8b949e# IFAC size, used in IFAC setup when the user
T8b949e# has not specified a custom IFAC size. This
T8b949e# option is specified in bytes.
Te6edf3DEFAULT_IFAC_SIZE Tff7b72= T79c0ff8
T8b949e# The following properties are local to this
T8b949e# particular interface implementation.
Te6edf3owner Tff7b72= Tff7b72None
Te6edf3port Tff7b72= Tff7b72None
Te6edf3speed Tff7b72= Tff7b72None
Te6edf3databits Tff7b72= Tff7b72None
Te6edf3parity Tff7b72= Tff7b72None
Te6edf3stopbits Tff7b72= Tff7b72None
Te6edf3serial Tff7b72= Tff7b72None
T8b949e# All Reticulum interfaces must have an __init__
T8b949e# method that takes 2 positional arguments:
T8b949e# The owner RNS Transport instance, and a dict
T8b949e# of configuration values.
Tff7b72def Tff7b72__init__Tb4b4b4(Tff7b72selfTb4b4b4, Te6edf3ownerTb4b4b4, Te6edf3configurationTb4b4b4)Tb4b4b4:
T8b949e# The following lines demonstrate handling
T8b949e# potential dependencies required for the
T8b949e# interface to function correctly.
Tff7b72import T7ee787importlib
Tff7b72if Te6edf3importlibTff7b72.Td2a8ffutilTff7b72.Td2a8fffind_specTb4b4b4(Ta5d6ff'Ta5d6ffserialTa5d6ff'Tb4b4b4) Tff7b72!= Tff7b72NoneTb4b4b4:
Tff7b72import T7ee787serial
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffUsing this interface requires a serial communication module to be installed.Ta5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_CRITICALTb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffYou can install one with the command: python3 -m pip install pyserialTa5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_CRITICALTb4b4b4)
Te6edf3RNSTff7b72.Td2a8ffpanicTb4b4b4(Tb4b4b4)
T8b949e# We start out by initialising the super-class
Tffa657superTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ff__init__Tb4b4b4(Tb4b4b4)
T8b949e# To make sure the configuration data is in the
T8b949e# correct format, we parse it through the following
T8b949e# method on the generic Interface class. This step
T8b949e# is required to ensure compatibility on all the
T8b949e# platforms that Reticulum supports.
Te6edf3ifconf Tff7b72= Te6edf3InterfaceTff7b72.Td2a8ffget_config_objTb4b4b4(Te6edf3configurationTb4b4b4)
T8b949e# Read the interface name from the configuration
T8b949e# and set it on our interface instance.
Te6edf3name Tff7b72= Te6edf3ifconfTb4b4b4[Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4]
Tff7b72selfTff7b72.Td2a8ffname Tff7b72= Te6edf3name
T8b949e# We read configuration parameters from the supplied
T8b949e# configuration data, and provide default values in
T8b949e# case any are missing.
Te6edf3port Tff7b72= Te6edf3ifconfTb4b4b4[Ta5d6ff"Ta5d6ffportTa5d6ff"Tb4b4b4] Tff7b72if Ta5d6ff"Ta5d6ffportTa5d6ff" Tff7b72in Te6edf3ifconf Tff7b72else Tff7b72None
Te6edf3speed Tff7b72= Tffa657intTb4b4b4(Te6edf3ifconfTb4b4b4[Ta5d6ff"Ta5d6ffspeedTa5d6ff"Tb4b4b4]Tb4b4b4) Tff7b72if Ta5d6ff"Ta5d6ffspeedTa5d6ff" Tff7b72in Te6edf3ifconf Tff7b72else T79c0ff9600
Te6edf3databits Tff7b72= Tffa657intTb4b4b4(Te6edf3ifconfTb4b4b4[Ta5d6ff"Ta5d6ffdatabitsTa5d6ff"Tb4b4b4]Tb4b4b4) Tff7b72if Ta5d6ff"Ta5d6ffdatabitsTa5d6ff" Tff7b72in Te6edf3ifconf Tff7b72else T79c0ff8
Te6edf3parity Tff7b72= Te6edf3ifconfTb4b4b4[Ta5d6ff"Ta5d6ffparityTa5d6ff"Tb4b4b4] Tff7b72if Ta5d6ff"Ta5d6ffparityTa5d6ff" Tff7b72in Te6edf3ifconf Tff7b72else Ta5d6ff"Ta5d6ffNTa5d6ff"
Te6edf3stopbits Tff7b72= Tffa657intTb4b4b4(Te6edf3ifconfTb4b4b4[Ta5d6ff"Ta5d6ffstopbitsTa5d6ff"Tb4b4b4]Tb4b4b4) Tff7b72if Ta5d6ff"Ta5d6ffstopbitsTa5d6ff" Tff7b72in Te6edf3ifconf Tff7b72else T79c0ff1
T8b949e# In case no port is specified, we abort setup by
T8b949e# raising an exception.
Tff7b72if Te6edf3port Tff7b72== Tff7b72NoneTb4b4b4:
Tff7b72raise Tf85149ValueErrorTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffNo port specified for Tffd700{Tff7b72selfTffd700}Ta5d6ff"Tb4b4b4)
T8b949e# All interfaces must supply a hardware MTU value
T8b949e# to the RNS Transport instance. This value should
T8b949e# be the maximum data packet payload size that the
T8b949e# underlying medium is capable of handling in all
T8b949e# cases without any segmentation.
Tff7b72selfTff7b72.Td2a8ffHW_MTU Tff7b72= T79c0ff564
T8b949e# We initially set the "online" property to false,
T8b949e# since the interface has not actually been fully
T8b949e# initialised and connected yet.
Tff7b72selfTff7b72.Td2a8ffonline Tff7b72= Tff7b72False
T8b949e# In this case, we can also set the indicated bit-
T8b949e# rate of the interface to the serial port speed.
Tff7b72selfTff7b72.Td2a8ffbitrate Tff7b72= Te6edf3speed
T8b949e# Configure internal properties on the interface
T8b949e# according to the supplied configuration.
Tff7b72selfTff7b72.Td2a8ffpyserial Tff7b72= Te6edf3serial
Tff7b72selfTff7b72.Td2a8ffserial Tff7b72= Tff7b72None
Tff7b72selfTff7b72.Td2a8ffowner Tff7b72= Te6edf3owner
Tff7b72selfTff7b72.Td2a8ffport Tff7b72= Te6edf3port
Tff7b72selfTff7b72.Td2a8ffspeed Tff7b72= Te6edf3speed
Tff7b72selfTff7b72.Td2a8ffdatabits Tff7b72= Te6edf3databits
Tff7b72selfTff7b72.Td2a8ffparity Tff7b72= Te6edf3serialTff7b72.Td2a8ffPARITY_NONE
Tff7b72selfTff7b72.Td2a8ffstopbits Tff7b72= Te6edf3stopbits
Tff7b72selfTff7b72.Td2a8fftimeout Tff7b72= T79c0ff100
Tff7b72if Te6edf3parityTff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4) Tff7b72== Ta5d6ff"Ta5d6ffeTa5d6ff" Tff7b72or Te6edf3parityTff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4) Tff7b72== Ta5d6ff"Ta5d6ffevenTa5d6ff"Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffparity Tff7b72= Te6edf3serialTff7b72.Td2a8ffPARITY_EVEN
Tff7b72if Te6edf3parityTff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4) Tff7b72== Ta5d6ff"Ta5d6ffoTa5d6ff" Tff7b72or Te6edf3parityTff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4) Tff7b72== Ta5d6ff"Ta5d6ffoddTa5d6ff"Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffparity Tff7b72= Te6edf3serialTff7b72.Td2a8ffPARITY_ODD
T8b949e# Since all required parameters are now configured,
T8b949e# we will try opening the serial port.
Tff7b72tryTb4b4b4:
Tff7b72selfTff7b72.Td2a8ffopen_portTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffCould not open serial port for interface Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4)
Tff7b72raise Te6edf3e
T8b949e# If opening the port succeeded, run any post-open
T8b949e# configuration required.
Tff7b72if Tff7b72selfTff7b72.Td2a8ffserialTff7b72.Td2a8ffis_openTb4b4b4:
Tff7b72selfTff7b72.Td2a8ffconfigure_deviceTb4b4b4(Tb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72raise Tf85149IOErrorTb4b4b4(Ta5d6ff"Ta5d6ffCould not open serial portTa5d6ff"Tb4b4b4)
T8b949e# Open the serial port with supplied configuration
T8b949e# parameters and store a reference to the open port.
Tff7b72def Td2a8ffopen_portTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffOpening serial port Ta5d6ff"Tff7b72+Tff7b72selfTff7b72.Td2a8ffportTff7b72+Ta5d6ff"Ta5d6ff...Ta5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_VERBOSETb4b4b4)
Tff7b72selfTff7b72.Td2a8ffserial Tff7b72= Tff7b72selfTff7b72.Td2a8ffpyserialTff7b72.Td2a8ffSerialTb4b4b4(
Te6edf3port Tff7b72= Tff7b72selfTff7b72.Td2a8ffportTb4b4b4,
Te6edf3baudrate Tff7b72= Tff7b72selfTff7b72.Td2a8ffspeedTb4b4b4,
Te6edf3bytesize Tff7b72= Tff7b72selfTff7b72.Td2a8ffdatabitsTb4b4b4,
Te6edf3parity Tff7b72= Tff7b72selfTff7b72.Td2a8ffparityTb4b4b4,
Te6edf3stopbits Tff7b72= Tff7b72selfTff7b72.Td2a8ffstopbitsTb4b4b4,
Te6edf3xonxoff Tff7b72= Tff7b72FalseTb4b4b4,
Te6edf3rtscts Tff7b72= Tff7b72FalseTb4b4b4,
Te6edf3timeout Tff7b72= T79c0ff0Tb4b4b4,
Te6edf3inter_byte_timeout Tff7b72= Tff7b72NoneTb4b4b4,
Te6edf3write_timeout Tff7b72= Tff7b72NoneTb4b4b4,
Te6edf3dsrdtr Tff7b72= Tff7b72FalseTb4b4b4,
Tb4b4b4)
T8b949e# The only thing required after opening the port
T8b949e# is to wait a small amount of time for the
T8b949e# hardware to initialise and then start a thread
T8b949e# that reads any incoming data from the device.
Tff7b72def Td2a8ffconfigure_deviceTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3sleepTb4b4b4(T79c0ff0.5Tb4b4b4)
Te6edf3thread Tff7b72= Te6edf3threadingTff7b72.Td2a8ffThreadTb4b4b4(Te6edf3targetTff7b72=Tff7b72selfTff7b72.Td2a8ffread_loopTb4b4b4)
Te6edf3threadTff7b72.Td2a8ffdaemon Tff7b72= Tff7b72True
Te6edf3threadTff7b72.Td2a8ffstartTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffonline Tff7b72= Tff7b72True
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffSerial port Ta5d6ff"Tff7b72+Tff7b72selfTff7b72.Td2a8ffportTff7b72+Ta5d6ff"Ta5d6ff is now openTa5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_VERBOSETb4b4b4)
T8b949e# This method will be called from our read-loop
T8b949e# whenever a full packet has been received over
T8b949e# the underlying medium.
Tff7b72def Td2a8ffprocess_incomingTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3dataTb4b4b4)Tb4b4b4:
T8b949e# Update our received bytes counter
Tff7b72selfTff7b72.Td2a8ffrxb Tff7b72+Tff7b72= Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4)
T8b949e# And send the data packet to the Transport
T8b949e# instance for processing.
Tff7b72selfTff7b72.Td2a8ffownerTff7b72.Td2a8ffinboundTb4b4b4(Te6edf3dataTb4b4b4, Tff7b72selfTb4b4b4)
T8b949e# The running Reticulum Transport instance will
T8b949e# call this method on the interface whenever the
T8b949e# interface must transmit a packet.
Tff7b72def Td2a8ffprocess_outgoingTb4b4b4(Tff7b72selfTb4b4b4,Te6edf3dataTb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72selfTff7b72.Td2a8ffonlineTb4b4b4:
T8b949e# First, escape and packetize the data
T8b949e# according to HDLC framing.
Te6edf3data Tff7b72= Tffa657bytesTb4b4b4(Tb4b4b4[Te6edf3HDLCTff7b72.Td2a8ffFLAGTb4b4b4]Tb4b4b4)Tff7b72+Te6edf3HDLCTff7b72.Td2a8ffescapeTb4b4b4(Te6edf3dataTb4b4b4)Tff7b72+Tffa657bytesTb4b4b4(Tb4b4b4[Te6edf3HDLCTff7b72.Td2a8ffFLAGTb4b4b4]Tb4b4b4)
T8b949e# Then write the framed data to the port
Te6edf3written Tff7b72= Tff7b72selfTff7b72.Td2a8ffserialTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3dataTb4b4b4)
T8b949e# Update the transmitted bytes counter
T8b949e# and ensure that all data was written
Tff7b72selfTff7b72.Td2a8fftxb Tff7b72+Tff7b72= Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4)
Tff7b72if Te6edf3written Tff7b72!= Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4)Tb4b4b4:
Tff7b72raise Tf85149IOErrorTb4b4b4(Ta5d6ff"Ta5d6ffSerial interface only wrote Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3writtenTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff bytes of Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4)Tb4b4b4)Tb4b4b4)
T8b949e# This read loop runs in a thread and continously
T8b949e# receives bytes from the underlying serial port.
T8b949e# When a full packet has been received, it will
T8b949e# be sent to the process_incoming methed, which
T8b949e# will in turn pass it to the Transport instance.
Tff7b72def Td2a8ffread_loopTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3in_frame Tff7b72= Tff7b72False
Te6edf3escape Tff7b72= Tff7b72False
Te6edf3data_buffer Tff7b72= Ta5d6ffbTa5d6ff"Ta5d6ff"
Te6edf3last_read_ms Tff7b72= Tffa657intTb4b4b4(Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)Tff7b72*T79c0ff1000Tb4b4b4)
Tff7b72while Tff7b72selfTff7b72.Td2a8ffserialTff7b72.Td2a8ffis_openTb4b4b4:
Tff7b72if Tff7b72selfTff7b72.Td2a8ffserialTff7b72.Td2a8ffin_waitingTb4b4b4:
Te6edf3byte Tff7b72= Tffa657ordTb4b4b4(Tff7b72selfTff7b72.Td2a8ffserialTff7b72.Td2a8ffreadTb4b4b4(T79c0ff1Tb4b4b4)Tb4b4b4)
Te6edf3last_read_ms Tff7b72= Tffa657intTb4b4b4(Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)Tff7b72*T79c0ff1000Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3in_frame Tff7b72and Te6edf3byte Tff7b72== Te6edf3HDLCTff7b72.Td2a8ffFLAGTb4b4b4)Tb4b4b4:
Te6edf3in_frame Tff7b72= Tff7b72False
Tff7b72selfTff7b72.Td2a8ffprocess_incomingTb4b4b4(Te6edf3data_bufferTb4b4b4)
Tff7b72elif Tb4b4b4(Te6edf3byte Tff7b72== Te6edf3HDLCTff7b72.Td2a8ffFLAGTb4b4b4)Tb4b4b4:
Te6edf3in_frame Tff7b72= Tff7b72True
Te6edf3data_buffer Tff7b72= Ta5d6ffbTa5d6ff"Ta5d6ff"
Tff7b72elif Tb4b4b4(Te6edf3in_frame Tff7b72and Tffa657lenTb4b4b4(Te6edf3data_bufferTb4b4b4) Tff7b72< Tff7b72selfTff7b72.Td2a8ffHW_MTUTb4b4b4)Tb4b4b4:
Tff7b72if Tb4b4b4(Te6edf3byte Tff7b72== Te6edf3HDLCTff7b72.Td2a8ffESCTb4b4b4)Tb4b4b4:
Te6edf3escape Tff7b72= Tff7b72True
Tff7b72elseTb4b4b4:
Tff7b72if Tb4b4b4(Te6edf3escapeTb4b4b4)Tb4b4b4:
Tff7b72if Tb4b4b4(Te6edf3byte Tff7b72== Te6edf3HDLCTff7b72.Td2a8ffFLAG Tff7b72^ Te6edf3HDLCTff7b72.Td2a8ffESC_MASKTb4b4b4)Tb4b4b4:
Te6edf3byte Tff7b72= Te6edf3HDLCTff7b72.Td2a8ffFLAG
Tff7b72if Tb4b4b4(Te6edf3byte Tff7b72== Te6edf3HDLCTff7b72.Td2a8ffESC Tff7b72^ Te6edf3HDLCTff7b72.Td2a8ffESC_MASKTb4b4b4)Tb4b4b4:
Te6edf3byte Tff7b72= Te6edf3HDLCTff7b72.Td2a8ffESC
Te6edf3escape Tff7b72= Tff7b72False
Te6edf3data_buffer Tff7b72= Te6edf3data_bufferTff7b72+Tffa657bytesTb4b4b4(Tb4b4b4[Te6edf3byteTb4b4b4]Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3time_since_last Tff7b72= Tffa657intTb4b4b4(Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)Tff7b72*T79c0ff1000Tb4b4b4) Tff7b72- Te6edf3last_read_ms
Tff7b72if Tffa657lenTb4b4b4(Te6edf3data_bufferTb4b4b4) Tff7b72> T79c0ff0 Tff7b72and Te6edf3time_since_last Tff7b72> Tff7b72selfTff7b72.Td2a8fftimeoutTb4b4b4:
Te6edf3data_buffer Tff7b72= Ta5d6ffbTa5d6ff"Ta5d6ff"
Te6edf3in_frame Tff7b72= Tff7b72False
Te6edf3escape Tff7b72= Tff7b72False
Te6edf3sleepTb4b4b4(T79c0ff0.08Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72selfTff7b72.Td2a8ffonline Tff7b72= Tff7b72False
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffA serial port error occurred, the contained exception was: Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe interface Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Tff7b72selfTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff experienced an unrecoverable error and is now offline.Ta5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4)
Tff7b72if Te6edf3RNSTff7b72.Td2a8ffReticulumTff7b72.Td2a8ffpanic_on_interface_errorTb4b4b4:
Te6edf3RNSTff7b72.Td2a8ffpanicTb4b4b4(Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffReticulum will attempt to reconnect the interface periodically.Ta5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffonline Tff7b72= Tff7b72False
Tff7b72selfTff7b72.Td2a8ffserialTff7b72.Td2a8ffcloseTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffreconnect_portTb4b4b4(Tb4b4b4)
T8b949e# This method handles serial port disconnects.
Tff7b72def Td2a8ffreconnect_portTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72while Tff7b72not Tff7b72selfTff7b72.Td2a8ffonlineTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff5Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffAttempting to reconnect serial port Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Tff7b72selfTff7b72.Td2a8ffportTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff for Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Tff7b72selfTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff...Ta5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_VERBOSETb4b4b4)
Tff7b72selfTff7b72.Td2a8ffopen_portTb4b4b4(Tb4b4b4)
Tff7b72if Tff7b72selfTff7b72.Td2a8ffserialTff7b72.Td2a8ffis_openTb4b4b4:
Tff7b72selfTff7b72.Td2a8ffconfigure_deviceTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffError while reconnecting port, the contained exception was: Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffReconnected serial port for Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4)
T8b949e# Signal to Reticulum that this interface should
T8b949e# not perform any ingress limiting.
Tff7b72def Td2a8ffshould_ingress_limitTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72False
T8b949e# We must provide a string representation of this
T8b949e# interface, that is used whenever the interface
T8b949e# is printed in logs or external programs.
Tff7b72def Tff7b72__str__Tb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72return Ta5d6ff"Ta5d6ffExampleInterface[Ta5d6ff"Tff7b72+Tff7b72selfTff7b72.Td2a8ffnameTff7b72+Ta5d6ff"Ta5d6ff]Ta5d6ff"
T8b949e# Finally, register the defined interface class as the
T8b949e# target class for Reticulum to use as an interface
Te6edf3interface_class Tff7b72= Te6edf3ExampleInterface
This example can also be found at https://github.com/markqvist/Reticulum/blob/master/Examples/ExampleInterface.py.
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────